diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4dc368a..28d19ce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,16 +1,16 @@ -name: baseSteam 1.17 CI +name: baseSteam 1.18 CI on: push: - branches: [master-1.17.1, 'feature/**'] + branches: [master-1.18.2, feature/basesteam-1.18.2-port] pull_request: - branches: [master-1.17.1] + branches: [master-1.18.2] permissions: contents: read concurrency: - group: basesteam-1.17-${{ github.workflow }}-${{ github.ref }} + group: basesteam-1.18-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: @@ -28,22 +28,29 @@ jobs: power_ref=$(sed -n 's/^poweradvantage_source_ref=//p' gradle.properties | tr -d '\r') power_hash=$(sed -n 's/^poweradvantage_deobf_sha256=//p' gradle.properties | tr -d '\r') if [[ ! "$power_ref" =~ ^[a-f0-9]{40}$ || ! "$power_hash" =~ ^[A-F0-9]{64}$ ]]; then - echo "::error::The exact basePower 1.17 dependency pin is pending." + echo "::error::The exact basePower 1.18 dependency pin is pending." exit 1 fi bm_ref=$(sed -n 's/^basemetals_source_commit=//p' gradle.properties | tr -d '\r') bm_hash=$(sed -n 's/^basemetals_deobf_sha256=//p' gradle.properties | tr -d '\r') if [[ ! "$bm_ref" =~ ^[a-f0-9]{40}$ || ! "$bm_hash" =~ ^[A-F0-9]{64}$ ]]; then - echo "::error::The exact Base Metals 1.17 dependency pin is pending." + echo "::error::The exact Base Metals 1.18 dependency pin is pending." exit 1 fi + os_ref=$(sed -n 's/^orespawn_source_commit=//p' gradle.properties | tr -d '\r') + os_hash=$(sed -n 's/^orespawn_deobf_sha256=//p' gradle.properties | tr -d '\r') + if [[ ! "$os_ref" =~ ^[a-f0-9]{40}$ || ! "$os_hash" =~ ^[A-F0-9]{64}$ ]]; then + echo "::error::The exact OreSpawn 1.18 dependency pin is pending." + exit 1 + fi + echo "ORESPAWN_SOURCE_REF=$os_ref" >> "$GITHUB_ENV" echo "POWERADVANTAGE_SOURCE_REF=$power_ref" >> "$GITHUB_ENV" echo "BASEMETALS_SOURCE_REF=$bm_ref" >> "$GITHUB_ENV" - name: Check out pinned OreSpawn uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: repository: MinecraftModDevelopmentMods/OreSpawn - ref: b56e2b62a1a6ea4d1dd96f8646a87801a817dbdf + ref: ${{ env.ORESPAWN_SOURCE_REF }} path: .ci-dependencies/OreSpawn - name: Use canonical LF dependency inputs # Use the same source bytes on hosted and local dependency builds. @@ -54,7 +61,7 @@ jobs: repository: MinecraftModDevelopmentMods/BaseMetals ref: ${{ env.BASEMETALS_SOURCE_REF }} path: .ci-dependencies/BaseMetals - - name: Check out basePower 1.17.1 + - name: Check out basePower 1.18.2 uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: repository: MinecraftModDevelopmentMods/basePower @@ -70,11 +77,6 @@ jobs: with: distribution: temurin java-version: '8.0.502+7' - - name: Install pinned Java 16 production toolchain - uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 - with: - distribution: temurin - java-version: '16.0.2+7' - name: Install Java 17 for Gradle uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 with: @@ -84,12 +86,12 @@ jobs: # These are Gradle project properties, not JVM system properties. shell: bash run: >- - echo "MMD_GRADLE_TOOLCHAINS=-Porg.gradle.java.installations.paths=$JAVA_HOME,$JAVA_HOME_16_X64,$JAVA_HOME_8_X64,$JAVA_HOME_25_X64 + echo "MMD_GRADLE_TOOLCHAINS=-Porg.gradle.java.installations.paths=$JAVA_HOME,$JAVA_HOME_8_X64,$JAVA_HOME_25_X64 -Porg.gradle.java.installations.auto-detect=false -Porg.gradle.java.installations.auto-download=false" >> "$GITHUB_ENV" - name: Set up Gradle uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6 - - name: Build 1.17.1 dependency chain + - name: Build 1.18.2 dependency chain id: dependencies shell: bash run: | @@ -97,22 +99,25 @@ jobs: orespawn="$GITHUB_WORKSPACE/.ci-dependencies/OreSpawn" basemetals="$GITHUB_WORKSPACE/.ci-dependencies/BaseMetals" power="$GITHUB_WORKSPACE/.ci-dependencies/basePower" + test "$(git -C "$orespawn" rev-parse HEAD)" = "$ORESPAWN_SOURCE_REF" test "$(git -C "$power" rev-parse HEAD)" = "$POWERADVANTAGE_SOURCE_REF" test "$(git -C "$basemetals" rev-parse HEAD)" = "$BASEMETALS_SOURCE_REF" chmod +x "$orespawn/gradlew" "$basemetals/gradlew" "$power/gradlew" (cd "$orespawn" && ./gradlew $MMD_GRADLE_TOOLCHAINS jar --no-daemon --stacktrace) (cd "$basemetals" && ./gradlew $MMD_GRADLE_TOOLCHAINS jar --no-daemon --stacktrace) - osjar="$orespawn/build/libs-dev/OreSpawn-4.1.0.117011-deobf.jar" - bmjar="$basemetals/build/libs-dev/BaseMetals-3.1.1.117011-deobf.jar" + osjar="$orespawn/build/libs-dev/OreSpawn-4.1.0.118021-deobf.jar" + bmjar="$basemetals/build/libs-dev/BaseMetals-3.1.2.118021-deobf.jar" (cd "$power" && ./gradlew $MMD_GRADLE_TOOLCHAINS jar unitBootstrapJar verifyReleaseDependencies \ -PbaseMetalsDeobfJar="$bmjar" -PoreSpawnDeobfJar="$osjar" \ --no-daemon --stacktrace) - powerjar="$power/build/libs-dev/basePower-2.4.9.117011-deobf.jar" + powerjar="$power/build/libs-dev/basePower-2.4.10.118021-deobf.jar" bootstrap="$power/build/unit-bootstrap/forge-native-unit-bootstrap.jar" test -f "$osjar" && test -f "$bmjar" && test -f "$powerjar" && test -f "$bootstrap" - release="$RUNNER_TEMP/basePower-2.4.9.117011.jar" + version="$(sed -n 's/^poweradvantage_version=//p' gradle.properties | tr -d '\r')" + [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.118021$ ]] + release="$RUNNER_TEMP/basePower-$version.jar" curl --fail --silent --show-error --location --retry 3 --output "$release" \ - 'https://maven.moddev.zone/releases/zone/moddev/mc/basepower/basePower/2.4.9.117011/basePower-2.4.9.117011.jar' + "https://maven.moddev.zone/releases/zone/moddev/mc/basepower/basePower/$version/basePower-$version.jar" power_release_sha=$(sed -n 's/^poweradvantage_player_sha256=//p' gradle.properties | tr -d '\r') printf '%s %s\n' "$power_release_sha" "$release" | sha256sum --check echo "orespawn=$osjar" >> "$GITHUB_OUTPUT" @@ -131,20 +136,20 @@ jobs: - name: Upload audited release candidate uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: - name: baseSteam-1.17.1-${{ github.sha }} + name: baseSteam-1.18.2-${{ github.sha }} if-no-files-found: error retention-days: 30 path: | - build/libs/baseSteam-2.2.10.117011.jar - build/libs/baseSteam-2.2.10.117011-sources.jar - build/libs/baseSteam-2.2.10.117011-javadoc.jar + build/libs/baseSteam-2.2.11.118021.jar + build/libs/baseSteam-2.2.11.118021-sources.jar + build/libs/baseSteam-2.2.11.118021-javadoc.jar build/release/SHA256SUMS CHANGELOG.txt - name: Upload diagnostics on failure if: failure() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: - name: baseSteam-1.17.1-diagnostics-${{ github.sha }} + name: baseSteam-1.18.2-diagnostics-${{ github.sha }} if-no-files-found: ignore retention-days: 14 path: | diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index f1cd3f9..d17fa79 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -2,9 +2,9 @@ name: CodeQL on: push: - branches: [master-1.17.1, 'feature/**'] + branches: [master-1.18.2, feature/basesteam-1.18.2-port] pull_request: - branches: [master-1.17.1] + branches: [master-1.18.2] schedule: - cron: '53 7 * * 4' @@ -28,22 +28,29 @@ jobs: power_ref=$(sed -n 's/^poweradvantage_source_ref=//p' gradle.properties | tr -d '\r') power_hash=$(sed -n 's/^poweradvantage_deobf_sha256=//p' gradle.properties | tr -d '\r') if [[ ! "$power_ref" =~ ^[a-f0-9]{40}$ || ! "$power_hash" =~ ^[A-F0-9]{64}$ ]]; then - echo "::error::The exact basePower 1.17 dependency pin is pending." + echo "::error::The exact basePower 1.18 dependency pin is pending." exit 1 fi bm_ref=$(sed -n 's/^basemetals_source_commit=//p' gradle.properties | tr -d '\r') bm_hash=$(sed -n 's/^basemetals_deobf_sha256=//p' gradle.properties | tr -d '\r') if [[ ! "$bm_ref" =~ ^[a-f0-9]{40}$ || ! "$bm_hash" =~ ^[A-F0-9]{64}$ ]]; then - echo "::error::The exact Base Metals 1.17 dependency pin is pending." + echo "::error::The exact Base Metals 1.18 dependency pin is pending." exit 1 fi + os_ref=$(sed -n 's/^orespawn_source_commit=//p' gradle.properties | tr -d '\r') + os_hash=$(sed -n 's/^orespawn_deobf_sha256=//p' gradle.properties | tr -d '\r') + if [[ ! "$os_ref" =~ ^[a-f0-9]{40}$ || ! "$os_hash" =~ ^[A-F0-9]{64}$ ]]; then + echo "::error::The exact OreSpawn 1.18 dependency pin is pending." + exit 1 + fi + echo "ORESPAWN_SOURCE_REF=$os_ref" >> "$GITHUB_ENV" echo "POWERADVANTAGE_SOURCE_REF=$power_ref" >> "$GITHUB_ENV" echo "BASEMETALS_SOURCE_REF=$bm_ref" >> "$GITHUB_ENV" - name: Check out pinned OreSpawn uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: repository: MinecraftModDevelopmentMods/OreSpawn - ref: b56e2b62a1a6ea4d1dd96f8646a87801a817dbdf + ref: ${{ env.ORESPAWN_SOURCE_REF }} path: .ci-dependencies/OreSpawn - name: Use canonical LF dependency inputs # Use the same source bytes on hosted and local dependency builds. @@ -54,7 +61,7 @@ jobs: repository: MinecraftModDevelopmentMods/BaseMetals ref: ${{ env.BASEMETALS_SOURCE_REF }} path: .ci-dependencies/BaseMetals - - name: Check out basePower 1.17.1 + - name: Check out basePower 1.18.2 uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: repository: MinecraftModDevelopmentMods/basePower @@ -70,11 +77,6 @@ jobs: with: distribution: temurin java-version: '8.0.502+7' - - name: Install pinned Java 16 production toolchain - uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 - with: - distribution: temurin - java-version: '16.0.2+7' - name: Install Java 17 for Gradle uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 with: @@ -84,12 +86,12 @@ jobs: # These are Gradle project properties, not JVM system properties. shell: bash run: >- - echo "MMD_GRADLE_TOOLCHAINS=-Porg.gradle.java.installations.paths=$JAVA_HOME,$JAVA_HOME_16_X64,$JAVA_HOME_8_X64,$JAVA_HOME_25_X64 + echo "MMD_GRADLE_TOOLCHAINS=-Porg.gradle.java.installations.paths=$JAVA_HOME,$JAVA_HOME_8_X64,$JAVA_HOME_25_X64 -Porg.gradle.java.installations.auto-detect=false -Porg.gradle.java.installations.auto-download=false" >> "$GITHUB_ENV" - name: Set up Gradle uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6 - - name: Build 1.17.1 dependency chain + - name: Build 1.18.2 dependency chain id: dependencies shell: bash run: | @@ -97,17 +99,18 @@ jobs: orespawn="$GITHUB_WORKSPACE/.ci-dependencies/OreSpawn" basemetals="$GITHUB_WORKSPACE/.ci-dependencies/BaseMetals" power="$GITHUB_WORKSPACE/.ci-dependencies/basePower" + test "$(git -C "$orespawn" rev-parse HEAD)" = "$ORESPAWN_SOURCE_REF" test "$(git -C "$power" rev-parse HEAD)" = "$POWERADVANTAGE_SOURCE_REF" test "$(git -C "$basemetals" rev-parse HEAD)" = "$BASEMETALS_SOURCE_REF" chmod +x "$orespawn/gradlew" "$basemetals/gradlew" "$power/gradlew" (cd "$orespawn" && ./gradlew $MMD_GRADLE_TOOLCHAINS jar --no-daemon --stacktrace) (cd "$basemetals" && ./gradlew $MMD_GRADLE_TOOLCHAINS jar --no-daemon --stacktrace) - osjar="$orespawn/build/libs-dev/OreSpawn-4.1.0.117011-deobf.jar" - bmjar="$basemetals/build/libs-dev/BaseMetals-3.1.1.117011-deobf.jar" + osjar="$orespawn/build/libs-dev/OreSpawn-4.1.0.118021-deobf.jar" + bmjar="$basemetals/build/libs-dev/BaseMetals-3.1.2.118021-deobf.jar" (cd "$power" && ./gradlew $MMD_GRADLE_TOOLCHAINS jar unitBootstrapJar verifyReleaseDependencies \ -PbaseMetalsDeobfJar="$bmjar" -PoreSpawnDeobfJar="$osjar" \ --no-daemon --stacktrace) - powerjar="$power/build/libs-dev/basePower-2.4.9.117011-deobf.jar" + powerjar="$power/build/libs-dev/basePower-2.4.10.118021-deobf.jar" bootstrap="$power/build/unit-bootstrap/forge-native-unit-bootstrap.jar" test -f "$osjar" && test -f "$bmjar" && test -f "$powerjar" && test -f "$bootstrap" echo "orespawn=$osjar" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/validate-gradle-build.yml b/.github/workflows/validate-gradle-build.yml index 0bf04b8..6252ede 100644 --- a/.github/workflows/validate-gradle-build.yml +++ b/.github/workflows/validate-gradle-build.yml @@ -3,11 +3,11 @@ name: Validate Gradle Wrapper on: push: branches: - - master-1.17.1 - - 'feature/**' + - master-1.18.2 + - feature/basesteam-1.18.2-port pull_request: branches: - - master-1.17.1 + - master-1.18.2 permissions: contents: read diff --git a/.gitignore b/.gitignore index cb99cf1..3cbc55f 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ *.class *.jar !/gradle/wrapper/gradle-wrapper.jar +!/ci-fixtures/tools/minecraft-mavenizer-0.5.21-orespawn-1.18.jar *.zip # Machine-specific agent context diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 8cf1e52..b3e36b8 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,4 +1,10 @@ -baseSteam 2.2.10.117011 (Minecraft 1.17.1) +baseSteam 2.2.11.118021 (Minecraft 1.18.2) + +- Bring baseSteam to Minecraft 1.18.2, with the same recipes and steam machines. +- Keep saved inventories, steam, drill state and musket wear when upgrading. +- Use the released basePower 2.4.10.118021 and Base Metals 3.1.2 save hook. + +baseSteam 2.2.10.117011 (local Minecraft 1.17.1 candidate) - Steam Advantage is now baseSteam. Install it with basePower 2.4.9.117011 and find its blocks and items in the shared basePower creative tab. - Recognize older Steam Advantage world IDs while keeping baseSteam registry paths, machine data and legacy save repairs. diff --git a/README.md b/README.md index a4f0664..6a045a2 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ # baseSteam -**Minecraft 1.17.1 (Forge), version 2.2.10.117011. Local candidate.** +**Minecraft 1.18.2 (Forge), version 2.2.11.118021.** baseSteam, formerly Steam Advantage, adds boilers, steam-powered machines, pumps, storage, elevators and a blackpowder musket to basePower. Its items and -blocks use the shared basePower creative tab. This version is not published. +blocks use the shared basePower creative tab. ## Upgrading a World Back up your world and test the upgrade on a copy. Replace Steam Advantage and Power Advantage with baseSteam and basePower; do not install both sets of mods. The migration recognizes old Steam Advantage IDs and keeps the registry paths, -machine settings and saved data used by earlier baseSteam versions. The -Minecraft 1.17.1 world upgrade still needs verification for this candidate. +machine settings and saved data used by earlier baseSteam versions. The Works +rig checks copied legacy saves, running machinery and cold restarts. Existing `basesteam-common.toml` settings take precedence. When that file is absent, baseSteam can copy `steamadvantage-common.toml` or import settings from an @@ -20,36 +20,38 @@ older `.cfg` file. The old config is left in place. ## Build Target -Minecraft 1.17.1, Forge 37.1.1, official 1.17.1 mappings and MCP -20210706.113038. ForgeGradle 7.0.34 and Gradle 9.6.1 run on Java 17. -Production compilation and launches use Temurin 16.0.2+7, class major 60. -Mavenizer uses Java 25. Java 8 is available only for older build utilities. +Minecraft 1.18.2, Forge 40.3.0, official 1.18.2 mappings and MCP +20220404.173914. ForgeGradle 7.0.34 and Gradle 9.6.1 run on Java 17. +Production compilation and launches use Temurin 17, class major 61. +The build-only Mavenizer fixture uses Java 25 and corrects the known Forge 40 +`Holder.Direct` decompile issue. Java 8 is only for older build utilities. The mod ID and resource namespace are `basesteam`; the Java implementation namespace is `zone.moddev.mc.basesteam`. The shared `cyano.poweradvantage.api` names remain in use. -basePower 2.4.9.117011 is required. Base Metals 3.1.1.117011 remains optional -in Forge; the CurseForge install metadata lists basePower and Base Metals. -Build against the exact basePower source and development jar pinned in -`gradle.properties`. The player jar is the published -[basePower 2.4.9.117011 release](https://maven.moddev.zone/releases/zone/moddev/mc/basepower/basePower/2.4.9.117011/basePower-2.4.9.117011.jar). -The Base Metals startup fix is also published as 3.1.1.117011. OreSpawn -4.1.0.117011 remains in the dependency chain. No dependency is embedded. +basePower 2.4.10.118021 is required. Base Metals 3.1.2.118021 remains optional +in Forge and requires OreSpawn 4.1.0.118021 when installed. The CurseForge +install metadata lists basePower and Base Metals. No dependency is embedded. ## Local Verification Use the checked-in Gradle wrapper and supply explicit paths to the development -jars matching the pins in `gradle.properties`. -Runtime jars are not interchangeable with development jars. Use the test-only -Forge capability bootstrap from the same basePower source pin. Use a Steam-only -Gradle user home; do not build against a sibling checkout's cache or mutable jar. +jars matching the pins in `gradle.properties`. Use a Steam-only Gradle user +home and the test-only Forge capability bootstrap. Player JARs are distinct +from development and published JARs. Before a packaged smoke, run +`verifyPlayerDependencies` with explicit `powerAdvantagePlayerJar`, +`baseMetalsPlayerJar` and `oreSpawnPlayerJar` project properties. + +Use the source for the [basePower 2.4.10.118021 release](https://github.com/MinecraftModDevelopmentMods/basePower/releases/tag/2.4.10.118021) +to build the development jar and the test bootstrap. Use the published jar +for packaged testing; its checksum is pinned separately. For example, from the Steam checkout in PowerShell: ```powershell $env:JAVA_HOME = $env:JAVA17_HOME -$jdks = @($env:JAVA17_HOME, $env:JAVA16_HOME, $env:JAVA25_HOME, $env:JAVA8_HOME) -join ',' +$jdks = @($env:JAVA17_HOME, $env:JAVA25_HOME, $env:JAVA8_HOME) -join ',' $toolchains = @("-Porg.gradle.java.installations.paths=$jdks", '-Porg.gradle.java.installations.auto-detect=false', '-Porg.gradle.java.installations.auto-download=false') @@ -64,13 +66,12 @@ $env:GRADLE_USER_HOME = $env:BASESTEAM_GRADLE_USER_HOME ``` On other platforms use `./gradlew`. The artifact family is -`baseSteam-2.2.10.117011.jar`, plus sources and Javadoc. The development jar +`baseSteam-2.2.11.118021.jar`, plus sources and Javadoc. The development jar stays under `build/libs-dev`. `node tools/verify-native-port.js` and `node tools/generate-recipe-unlocks.js --check` -run without the dependency jars. The [port status](docs/PORT117-STATUS.md) separates -these checks from the pending build and runtime gates and retains the earlier -Steam Advantage evidence. +run without the dependency jars. [1.18.2 port status](docs/PORT118-STATUS.md) +tracks current checks. [1.17.1 port status](docs/PORT117-STATUS.md) is historical. ## Port Details @@ -78,7 +79,8 @@ Native block-entity factories preserve their actual constructor position and block state. Loading NBT does not replace that position, including when a drill moves along a track. Power's shared native ticker retains per-tick work before the eight-tick power update on both logical sides. The drill-bit helper has its -own native ticker. +own native ticker. Minecraft 1.18.2 uses `saveAdditional` for native saves, +and the drill-bit direction packet carries the native block-entity type. The port retains first-loaded-tick redstone sampling in the crusher and furnace, water-only boiler inputs, dirty-owner callbacks, finite energy and fluid diff --git a/build.gradle b/build.gradle index 9c4f653..d0f6212 100644 --- a/build.gradle +++ b/build.gradle @@ -14,6 +14,21 @@ plugins { id 'net.minecraftforge.gradle' version '7.0.34' } +def mavenizerFixture = file('ci-fixtures/tools/minecraft-mavenizer-0.5.21-orespawn-1.18.jar') +def mavenizerFixtureHash = '28A6697BDF6D9500EC0AF9C1C949B9F3ED7DDFDA649CB5CAC4BEEA2E9567570A' +if (!mavenizerFixture.isFile() || MessageDigest.getInstance('SHA-256').digest(mavenizerFixture.bytes) + .encodeHex().toString().toUpperCase(Locale.ROOT) != mavenizerFixtureHash) { + throw new GradleException('Missing or invalid Forge 40 Mavenizer compatibility fixture') +} +fgtools.configure('mavenizer') { + classpath.from(mavenizerFixture) + mainClass.set('net.minecraftforge.mcmaven.cli.Main') + javaLauncher.set(javaToolchains.launcherFor { + languageVersion = JavaLanguageVersion.of(25) + vendor = JvmVendorSpec.ADOPTIUM + }) +} + group = project.mod_group version = project.mod_version base.archivesName = 'baseSteam' @@ -44,7 +59,7 @@ ext.release_tag = project.mod_version java { toolchain { - languageVersion = JavaLanguageVersion.of(16) + languageVersion = JavaLanguageVersion.of(17) vendor = JvmVendorSpec.ADOPTIUM } withSourcesJar() @@ -53,11 +68,11 @@ java { tasks.withType(JavaCompile).configureEach { javaCompiler = javaToolchains.compilerFor { - languageVersion = JavaLanguageVersion.of(16) + languageVersion = JavaLanguageVersion.of(17) vendor = JvmVendorSpec.ADOPTIUM } - sourceCompatibility = JavaVersion.VERSION_16 - targetCompatibility = JavaVersion.VERSION_16 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 options.encoding = 'UTF-8' options.compilerArgs.addAll(['-Xmaxerrs', '1000']) } @@ -91,10 +106,6 @@ minecraft { mappings channel: project.mapping_channel, version: project.mapping_version String configuredRunDirectory = providers.gradleProperty('steamAdvantageRunDirectory') .getOrElse('run') - def forgeRunModClassesDirectory = layout.buildDirectory.dir('forge-run-mod-classes/main') - def forgeMainOutputPath = forgeRunModClassesDirectory.get().asFile.absolutePath - def mainModClasses = "${mod_id}%%${forgeMainOutputPath}${File.pathSeparator}${mod_id}%%${forgeMainOutputPath}" - def forge37RunTargets = [client: 'fmluserdevclient', server: 'fmluserdevserver', data: 'fmluserdevdata'] runs { configureEach { jvmArgs '--add-opens', 'java.base/java.lang.invoke=ALL-UNNAMED' @@ -102,13 +113,6 @@ minecraft { systemProperty 'forge.logging.markers', 'REGISTRIES' systemProperty 'forge.logging.console.level', 'info' mods { basesteam { source sourceSets.main } } - environment 'target', forge37RunTargets[name] - environment 'MCP_MAPPINGS', "${mapping_channel}_${mapping_version}" - environment 'MCP_VERSION', mcp_version - environment 'FORGE_VERSION', forge_version - environment 'FORGE_GROUP', 'net.minecraftforge' - environment 'MC_VERSION', minecraft_version - environment 'MOD_CLASSES', mainModClasses } register('client') register('server') { @@ -130,18 +134,19 @@ sourceSets.main.resources { def forgeRunModClassesDirectory = layout.buildDirectory.dir('forge-run-mod-classes/main') def prepareForgeRunModClasses = tasks.register('prepareForgeRunModClasses', Sync) { group = 'ide' - description = 'Merges classes and processed resources for Forge 1.17 launches.' + description = 'Merges classes and processed resources for Forge 40 launches.' dependsOn tasks.named('classes') from sourceSets.main.output into forgeRunModClassesDirectory } tasks.withType(JavaExec).configureEach { javaLauncher = javaToolchains.launcherFor { - languageVersion = JavaLanguageVersion.of(16) + languageVersion = JavaLanguageVersion.of(17) vendor = JvmVendorSpec.ADOPTIUM } if (name in ['runClient', 'runServer', 'runData']) { dependsOn prepareForgeRunModClasses + environment 'MOD_CLASSES', "${mod_id}%%${forgeRunModClassesDirectory.get().asFile.absolutePath}" } } @@ -165,31 +170,21 @@ repositories { } } -// Require explicit development jars so sibling builds cannot silently replace the inputs. -def requiredDeobfJar = { String property, String dependency -> - File jarFile = providers.gradleProperty(property).map { file(it) }.getOrNull() - if (jarFile == null || !jarFile.isFile()) { - throw new GradleException("Supply the pinned ${dependency} development jar via -P${property}=.") - } - jarFile -} -def powerAdvantageDeobfJar = requiredDeobfJar('powerAdvantageDeobfJar', 'basePower') -def baseMetalsDeobfJar = requiredDeobfJar('baseMetalsDeobfJar', 'Base Metals') -def oreSpawnDeobfJar = requiredDeobfJar('oreSpawnDeobfJar', 'OreSpawn') +// Only code and launch tasks require the exact jars; source/resource checks can run independently. +def explicitJar = { String property -> providers.gradleProperty(property).map { file(it) }.getOrNull() } +def powerAdvantageDeobfJar = explicitJar('powerAdvantageDeobfJar') +def baseMetalsDeobfJar = explicitJar('baseMetalsDeobfJar') +def oreSpawnDeobfJar = explicitJar('oreSpawnDeobfJar') +def developmentJars = files([powerAdvantageDeobfJar, baseMetalsDeobfJar, oreSpawnDeobfJar].findAll { it != null }) dependencies { implementation minecraft.dependency( "net.minecraftforge:forge:${project.minecraft_version}-${project.forge_version}") // basePower is required at runtime but remains a separate mod jar. - compileOnly files(powerAdvantageDeobfJar) - runtimeOnly files(powerAdvantageDeobfJar) - compileOnly files(baseMetalsDeobfJar) - runtimeOnly files(baseMetalsDeobfJar) - // Required transitively by the coordinated basePower/Base Metals development runtime. - compileOnly files(oreSpawnDeobfJar) - runtimeOnly files(oreSpawnDeobfJar) + compileOnly developmentJars + runtimeOnly developmentJars testImplementation 'junit:junit:4.13.2' - testImplementation files(powerAdvantageDeobfJar, baseMetalsDeobfJar, oreSpawnDeobfJar) + testImplementation developmentJars } tasks.named('processResources', ProcessResources) { @@ -268,7 +263,7 @@ def renamedJar = renamer.classes(tasks.named('jar', Jar)) { // Renamer adds an internal Fernflower parameter map. Repack its output so the // public jar is deterministic and contains only the mod plus its own manifest. def releaseJar = tasks.register('releaseJar', Jar) { - dependsOn renamedJar + dependsOn renamedJar, 'verifyReleaseDependencies' archiveClassifier = null destinationDirectory = layout.buildDirectory.dir('libs') from({ zipTree(renamedJar.get().output.get().asFile) }) { @@ -344,26 +339,33 @@ def expectedReleaseFiles = providers.provider { } def preparedReleaseDir = providers.gradleProperty('preparedReleaseDir') +def verifyPackFormats = { pack -> + if (pack.pack_format != 9 || pack['forge:resource_pack_format'] != 8 + || pack['forge:data_pack_format'] != 9) { + throw new GradleException('Minecraft 1.18.2 requires resource pack format 8 and data pack format 9') + } +} + tasks.register('verifyReleaseConfiguration') { group = 'verification' description = 'Validates release identity, target metadata, and dependency policy.' doLast { - if (new JsonSlurper().parse(file('src/main/resources/pack.mcmeta')).pack.pack_format != 7) { - throw new GradleException('Minecraft 1.17.1 resources must use pack format 7') - } - if (project.mod_version != '2.2.10.117011') { + verifyPackFormats(new JsonSlurper().parse(file('src/main/resources/pack.mcmeta')).pack) + if (project.mod_version != '2.2.11.118021') { throw new GradleException("Unexpected baseSteam release version: ${project.mod_version}") } - if (project.minecraft_version != '1.17.1' - || project.forge_version != '37.1.1' + if (project.minecraft_version != '1.18.2' || project.minecraft_version_range != '[1.18.2]' + || project.forge_version != '40.3.0' || project.forge_version_range != '[40.3.0,41)' + || project.loader_version_range != '[40,41)' || project.mapping_channel != 'official' - || project.mapping_version != '1.17.1' - || project.mcp_version != '20210706.113038') { + || project.mapping_version != '1.18.2' + || project.mcp_version != '20220404.173914') { throw new GradleException('Unexpected Minecraft, Forge, or mappings target') } if (project.loader_name != 'forge' || project.loader_code != '1' - || project.java_version != '16' - || project.java_toolchain_version != '16.0.2+7' + || project.java_version != '17' + || project.java_toolchain_version != '17.0.1+12' + || project.java_setup_version != '17.0.1+12' || project.gradle_java_version != '17') { throw new GradleException('Unexpected release dispatcher target metadata') } @@ -377,29 +379,27 @@ tasks.register('verifyReleaseConfiguration') { } if (project.cf_requirements != 'basepower,base-metals' || project.cf_optionals != '' - || project.cf_versions != '1.17.1') { + || project.cf_versions != '1.18.2') { throw new GradleException('Unexpected CurseForge dependency or game-version metadata') } - if (project.basemetals_version != '3.1.1.117011' - || project.orespawn_version != '4.1.0.117011' - || project.orespawn_source_commit != 'b56e2b62a1a6ea4d1dd96f8646a87801a817dbdf' - || project.orespawn_deobf_sha256 != 'CB7A736314842B110B2E4788FBD5E21D4AE8803C5ED34A49199B034BE572DA39') { - throw new GradleException('Unexpected published-source development API identity') + if (project.basemetals_version != '3.1.2.118021' + || project.orespawn_version != '4.1.0.118021') { + throw new GradleException('Unexpected development API versions') } - if (project.poweradvantage_version != '2.4.9.117011' + if (project.poweradvantage_version != '2.4.10.118021' || project.poweradvantage_source_repository != 'MinecraftModDevelopmentMods/basePower') { throw new GradleException('Unexpected basePower compile dependency identity') } String mainSource = file( 'src/main/java/zone/moddev/mc/basesteam/SteamAdvantage.java').getText('UTF-8') if (!mainSource.contains('MODID = "basesteam"') - || !mainSource.contains('VERSION = "2.2.10.117011"')) { + || !mainSource.contains('VERSION = "2.2.11.118021"')) { throw new GradleException('baseSteam source identity changed') } String metadata = file('src/main/resources/META-INF/mods.toml').getText('UTF-8').replace('\r\n', '\n') ['modId="basesteam"', 'displayName="baseSteam"', - 'modId="basepower"\nmandatory=true\nversionRange="[2.4.9.117011,3.0.0)"', - 'modId="basemetals"\nmandatory=false\nversionRange="[3.1.1.117011,4.0.0)"'].each { marker -> + 'modId="basepower"\nmandatory=true\nversionRange="[2.4.10.118021,3.0.0)"', + 'modId="basemetals"\nmandatory=false\nversionRange="[3.1.2.118021,4.0.0)"'].each { marker -> if (!metadata.contains(marker)) { throw new GradleException("Source mods.toml is missing ${marker}") } @@ -407,59 +407,105 @@ tasks.register('verifyReleaseConfiguration') { } } -tasks.register('verifyNativeDevelopmentDependencies') { - group = 'verification' - description = 'Resolves Forge and audits the exact development jars.' - doLast { - Properties pins = new Properties() - file('gradle.properties').withInputStream { pins.load(it) } - ['poweradvantage_source_ref', 'poweradvantage_deobf_sha256', - 'basemetals_source_commit', 'basemetals_deobf_sha256'].each { key -> - String pinned = pins.getProperty(key, '') - def pattern = key.endsWith('_sha256') ? /[A-F0-9]{64}/ : /[a-f0-9]{40}/ - if (!(pinned ==~ pattern) || project.property(key) != pinned) { - throw new GradleException("Supply the exact coordinated ${key} in gradle.properties; overrides are not accepted.") - } - } - String actualHash = MessageDigest.getInstance('SHA-256') - .digest(powerAdvantageDeobfJar.bytes).encodeHex().toString().toUpperCase() - if (actualHash != project.poweradvantage_deobf_sha256) { - throw new GradleException("basePower SHA-256 is ${actualHash}, expected " - + project.poweradvantage_deobf_sha256) - } - new ZipFile(powerAdvantageDeobfJar).withCloseable { zip -> - ['cyano/poweradvantage/api/PoweredEntity.class', - 'cyano/poweradvantage/api/GUIBlock.class', - 'cyano/poweradvantage/api/FluidCategoryRegistry.class', - 'cyano/poweradvantage/api/migration/LegacyMigrationApi.class', - 'zone/moddev/mc/basepower/conduitnetwork/ConduitRegistry.class', - 'zone/moddev/mc/basepower/init/Fluids.class', - 'zone/moddev/mc/basepower/init/ItemGroups.class'].each { required -> - if (zip.getEntry(required) == null) { - throw new GradleException("basePower dependency is missing ${required}") - } - } - def manifest = new Manifest(zip.getInputStream(zip.getEntry('META-INF/MANIFEST.MF'))).mainAttributes - if (manifest.getValue('Implementation-Version') != project.poweradvantage_version) { - throw new GradleException('basePower dependency manifest has the wrong version') - } - if (manifest.getValue('Built-On') != '1.17.1-37.1.1' - || manifest.getValue('Built-On-Java') != '16') { - throw new GradleException('basePower must be the native 1.17.1 Java 16 dependency') +def committedPin = { String key, pattern -> + Properties pins = new Properties() + file('gradle.properties').withInputStream { pins.load(it) } + String pinned = pins.getProperty(key, '') + if (!(pinned ==~ pattern) || project.property(key) != pinned) { + throw new GradleException("Supply the exact coordinated ${key} in gradle.properties; overrides are not accepted.") + } + pinned +} + +def auditJar = { File jarFile, String property, String hashPin, String modId, String expectedVersion, + List required -> + String expectedHash = committedPin(hashPin, /[A-F0-9]{64}/) + if (jarFile == null || !jarFile.isFile()) { + throw new GradleException("Supply the pinned ${modId} jar via -P${property}=.") + } + String actualHash = MessageDigest.getInstance('SHA-256') + .digest(jarFile.bytes).encodeHex().toString().toUpperCase(Locale.ROOT) + if (actualHash != expectedHash) { + throw new GradleException("${modId} SHA-256 is ${actualHash}, expected ${expectedHash}") + } + new ZipFile(jarFile).withCloseable { zip -> + (['META-INF/MANIFEST.MF', 'META-INF/mods.toml'] + required).each { entry -> + if (zip.getEntry(entry) == null) { + throw new GradleException("${modId} dependency is missing ${entry}") } } - String actualBaseMetalsHash = MessageDigest.getInstance('SHA-256') - .digest(baseMetalsDeobfJar.bytes).encodeHex().toString().toUpperCase() - if (actualBaseMetalsHash != project.basemetals_deobf_sha256) { - throw new GradleException("Base Metals SHA-256 is ${actualBaseMetalsHash}, expected " - + project.basemetals_deobf_sha256) + def manifest = new Manifest(zip.getInputStream(zip.getEntry('META-INF/MANIFEST.MF'))).mainAttributes + if (manifest.getValue('Implementation-Version') != expectedVersion + || manifest.getValue('Built-On') != '1.18.2-40.3.0' + || manifest.getValue('Built-On-Java') != '17') { + throw new GradleException("${modId} must be ${expectedVersion} for Forge 40.3.0 and Java 17") } - String actualOreSpawnHash = MessageDigest.getInstance('SHA-256') - .digest(oreSpawnDeobfJar.bytes).encodeHex().toString().toUpperCase() - if (actualOreSpawnHash != project.orespawn_deobf_sha256) { - throw new GradleException("OreSpawn SHA-256 is ${actualOreSpawnHash}, expected " - + project.orespawn_deobf_sha256) + String metadata = zip.getInputStream(zip.getEntry('META-INF/mods.toml')).getText('UTF-8') + if (!metadata.readLines().any { it.startsWith("modId=\"${modId}\"") }) { + throw new GradleException("${modId} dependency declares a different mod identity") } + } + logger.lifecycle("${modId}: ${jarFile.canonicalPath} SHA-256 ${actualHash}") +} + +def baseMetalsApi = ['zone/moddev/mc/basemetals/recipe/CrushingRecipe.class'] +def oreSpawnApi = ['zone/moddev/mc/orespawn/api/OreSpawnApi.class'] +def powerApi = [ + 'cyano/poweradvantage/api/PoweredEntity.class', + 'cyano/poweradvantage/api/GUIBlock.class', + 'cyano/poweradvantage/api/FluidCategoryRegistry.class', + 'cyano/poweradvantage/api/migration/LegacyMigrationApi.class', + 'cyano/poweradvantage/api/simple/TileEntitySimplePowerMachine.class', + 'zone/moddev/mc/basepower/conduitnetwork/ConduitRegistry.class', + 'zone/moddev/mc/basepower/init/Fluids.class', + 'zone/moddev/mc/basepower/init/ItemGroups.class', + 'zone/moddev/mc/basepower/registry/still/recipe/DistillationRecipeRegistry.class' +] + +def auditCompanions = { boolean player -> + String variant = player ? 'Player' : 'Deobf' + String pin = player ? 'player' : 'deobf' + committedPin('basemetals_source_commit', /[a-f0-9]{40}/) + committedPin('orespawn_source_commit', /[a-f0-9]{40}/) + auditJar(explicitJar("baseMetals${variant}Jar"), "baseMetals${variant}Jar", "basemetals_${pin}_sha256", + 'basemetals', project.basemetals_version, baseMetalsApi) + auditJar(explicitJar("oreSpawn${variant}Jar"), "oreSpawn${variant}Jar", "orespawn_${pin}_sha256", + 'orespawn', project.orespawn_version, oreSpawnApi) +} + +tasks.register('verifyCompanionDevelopmentDependencies') { + group = 'verification' + description = 'Audits the explicit Base Metals and OreSpawn development JARs independently of basePower.' + dependsOn tasks.named('verifyReleaseConfiguration') + doLast { auditCompanions(false) } +} + +tasks.register('verifyCompanionPlayerDependencies') { + group = 'verification' + description = 'Audits the explicit local Base Metals and OreSpawn player JARs; not a public-byte check.' + dependsOn tasks.named('verifyReleaseConfiguration') + doLast { auditCompanions(true) } +} + +tasks.register('verifyPlayerDependencies') { + group = 'verification' + description = 'Audits all exact player JARs before a packaged runtime check.' + dependsOn tasks.named('verifyCompanionPlayerDependencies') + doLast { + committedPin('poweradvantage_source_ref', /[a-f0-9]{40}/) + auditJar(explicitJar('powerAdvantagePlayerJar'), 'powerAdvantagePlayerJar', 'poweradvantage_player_sha256', + 'basepower', project.poweradvantage_version, powerApi) + } +} + +tasks.register('verifyNativeDevelopmentDependencies') { + group = 'verification' + description = 'Resolves Forge and audits the exact development JARs.' + dependsOn tasks.named('verifyCompanionDevelopmentDependencies') + doLast { + committedPin('poweradvantage_source_ref', /[a-f0-9]{40}/) + auditJar(powerAdvantageDeobfJar, 'powerAdvantageDeobfJar', 'poweradvantage_deobf_sha256', + 'basepower', project.poweradvantage_version, powerApi) Set compileFiles = configurations.compileClasspath.files.collect { it.canonicalFile } as Set if (!compileFiles.contains(powerAdvantageDeobfJar.canonicalFile)) { @@ -554,9 +600,7 @@ tasks.register('verifyReleaseArtifacts') { } def resourcePack = new JsonSlurper().parseText(zip.getInputStream(zip.getEntry('pack.mcmeta')) .getText(StandardCharsets.UTF_8.name())) - if (resourcePack.pack.pack_format != 7) { - throw new GradleException('Packaged resources must use native 1.17 pack format 7') - } + verifyPackFormats(resourcePack.pack) ['agent-notes/', '.codex/', 'src/test/', 'fernflower_abstract_parameter_names.txt', 'cyano/poweradvantage/', 'zone/moddev/mc/basepower/', 'zone/moddev/mc/poweradvantage/', 'zone/moddev/mc/steamadvantage/', 'assets/steamadvantage/', 'data/steamadvantage/', @@ -588,8 +632,8 @@ tasks.register('verifyReleaseArtifacts') { } } int major = ((header[6] & 0xff) << 8) | (header[7] & 0xff) - if (major != 60) { - throw new GradleException("${entry.name} uses Java class major ${major}, expected 60") + if (major != 61) { + throw new GradleException("${entry.name} uses Java class major ${major}, expected 61") } } } @@ -823,15 +867,10 @@ tasks.register('isolateEclipseProductionRuns') { } } } - // ForgeGradle 7's legacy 1.12 generator leaves this token unresolved, - // so write the target version into every generated launch explicitly. fileTree(project.projectDir) { include 'run*.launch' }.files.each { File launch -> String contents = launch.getText('UTF-8') - contents = contents.replace( - 'key="MC_VERSION" value="${MC_VERSION}"', - "key=\"MC_VERSION\" value=\"${project.minecraft_version}\"") if (launch.name in ['runClient.launch', 'runServer.launch', 'runData.launch']) { // Eclipse compiles into bin/main, not Gradle's merged run output. def launchXml = new groovy.xml.XmlParser().parseText(contents) @@ -842,8 +881,7 @@ tasks.register('isolateEclipseProductionRuns') { throw new GradleException("${launch.name} must define one MOD_CLASSES entry") } String mainOutput = file('bin/main').absolutePath - String eclipseModClasses = "${project.mod_id}%%${mainOutput}" + - "${File.pathSeparator}${project.mod_id}%%${mainOutput}" + String eclipseModClasses = "${project.mod_id}%%${mainOutput}" modClasses[0].@value = eclipseModClasses contents = groovy.xml.XmlUtil.serialize(launchXml) } @@ -949,16 +987,16 @@ tasks.register('verifyEclipseProductionClasspath') { } def eclipseClasspath = new groovy.xml.XmlSlurper().parse(file('.classpath')) if (!eclipseClasspath.classpathentry.any { - it.@kind.text() == 'con' && it.@path.text().tokenize('/').last() == 'JavaSE-16' + it.@kind.text() == 'con' && it.@path.text().tokenize('/').last() == 'JavaSE-17' }) { - throw new GradleException('Eclipse must use the Java 16 production execution environment') + throw new GradleException('Eclipse must use the Java 17 production execution environment') } Properties jdt = new Properties() file('.settings/org.eclipse.jdt.core.prefs').withInputStream { jdt.load(it) } ['org.eclipse.jdt.core.compiler.codegen.targetPlatform', 'org.eclipse.jdt.core.compiler.source', 'org.eclipse.jdt.core.compiler.compliance'].each { key -> - if (jdt.getProperty(key) != '16') { - throw new GradleException("Eclipse ${key} must remain Java 16") + if (jdt.getProperty(key) != '17') { + throw new GradleException("Eclipse ${key} must remain Java 17") } } def processedResources = eclipseClasspath.classpathentry.findAll { @@ -977,8 +1015,7 @@ tasks.register('verifyEclipseProductionClasspath') { if (file(mainOutput).canonicalFile != file('bin/main').canonicalFile) { throw new GradleException('Eclipse production classes must use bin/main') } - String expectedModClasses = "${project.mod_id}%%${file(mainOutput).absolutePath}" + - "${File.pathSeparator}${project.mod_id}%%${file(mainOutput).absolutePath}" + String expectedModClasses = "${project.mod_id}%%${file(mainOutput).absolutePath}" ['runClient.launch', 'runServer.launch', 'runData.launch'].each { name -> def launch = new groovy.xml.XmlSlurper().parse(file(name)) String modClasses = launch.mapAttribute.find { @@ -1000,7 +1037,7 @@ tasks.register('verifyEclipseProductionClasspath') { if (!arguments.contains('--main cpw.mods.bootstraplauncher.BootstrapLauncher') || !vmArguments.contains('--add-opens java.base/java.lang.invoke=ALL-UNNAMED') || contents.contains('net.minecraftforge.userdev.LaunchTesting')) { - throw new GradleException("${name} must retain the native Forge 37 bootstrap and Java 16 module access") + throw new GradleException("${name} must retain the native Forge 40 bootstrap and Java 17 module access") } if (launch.booleanAttribute.find { it.@key.text() == 'org.eclipse.jdt.launching.ATTR_EXCLUDE_TEST_CODE' @@ -1055,10 +1092,12 @@ tasks.register('verifyEclipseProductionClasspath') { throw new GradleException( "${name} does not preserve the Forge bootstrap classpath order") } - if (contents.contains('${MC_VERSION}') || launch.mapAttribute.find { - it.@key.text() == 'org.eclipse.debug.core.environmentVariables' - }.mapEntry.find { it.@key.text() == 'MC_VERSION' }.@value.text() != project.minecraft_version) { - throw new GradleException("${name} does not define a literal Minecraft version") + String target = name == 'runClient.launch' ? 'forgeclientuserdev' : 'forgeserveruserdev' + if (!arguments.contains("--launchTarget ${target}") + || !arguments.contains("--fml.mcVersion ${project.minecraft_version}") + || !arguments.contains("--fml.forgeVersion ${project.forge_version}") + || !arguments.contains("--fml.mcpVersion ${project.mcp_version}")) { + throw new GradleException("${name} does not target the exact Forge 40 development runtime") } } } @@ -1073,7 +1112,7 @@ tasks.register('verifyModelAssets') { File modelRoot = new File(assetRoot, 'models') File blockstateRoot = new File(assetRoot, 'blockstates') inputs.dir(assetRoot) - inputs.file(powerAdvantageDeobfJar) + inputs.files(providers.gradleProperty('powerAdvantageDeobfJar').map { file(it) }) doLast { fileTree(assetRoot) { include '**/*.json' }.files.sort().each { File jsonFile -> diff --git a/ci-fixtures/tools/LICENSE-MAVENIZER.txt b/ci-fixtures/tools/LICENSE-MAVENIZER.txt new file mode 100644 index 0000000..8000a6f --- /dev/null +++ b/ci-fixtures/tools/LICENSE-MAVENIZER.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/ci-fixtures/tools/README.md b/ci-fixtures/tools/README.md new file mode 100644 index 0000000..3408522 --- /dev/null +++ b/ci-fixtures/tools/README.md @@ -0,0 +1,24 @@ +# Forge 1.18.2 Mavenizer compatibility fixture + +`minecraft-mavenizer-0.5.21-orespawn-1.18.jar` is a build-only derivative of +MinecraftForge MinecraftMavenizer `0.5.21`, source commit +`6968241ce7a0a902cdc1c534b976e8373a423091`. + +Forge 40's 1.18.2 decompiler emits a redundant explicit `Holder.Direct#value` +record accessor. The complete patched Minecraft source tree is rejected by +modern `javac`; removing that method is safe because the record generates the +same public accessor automatically. The derivative also honors +`ORESPAWN_MAVENIZER_OFFLINE=true` by adding Mavenizer's native `--offline` +argument. This closes ForgeGradle 7's failure to propagate Gradle offline mode +to the subprocess; it does not alter normal online builds. + +`minecraft-1.18-holder-accessor.patch` documents the complete two-file source +change. + +The fixture is used only while ForgeGradle prepares the development dependency. +This copy comes from IAF118. It is checksum-verified by the baseSteam build +and excluded from every baseSteam release artifact. +MinecraftMavenizer remains licensed LGPL-2.1-only; see `LICENSE-MAVENIZER.txt`. + +SHA-256: +`28A6697BDF6D9500EC0AF9C1C949B9F3ED7DDFDA649CB5CAC4BEEA2E9567570A` diff --git a/ci-fixtures/tools/minecraft-1.18-holder-accessor.patch b/ci-fixtures/tools/minecraft-1.18-holder-accessor.patch new file mode 100644 index 0000000..57fd2b2 --- /dev/null +++ b/ci-fixtures/tools/minecraft-1.18-holder-accessor.patch @@ -0,0 +1,43 @@ +diff --git a/src/main/java/net/minecraftforge/mcmaven/cli/Main.java b/src/main/java/net/minecraftforge/mcmaven/cli/Main.java +index eeda8a0..4762951 100644 +--- a/src/main/java/net/minecraftforge/mcmaven/cli/Main.java ++++ b/src/main/java/net/minecraftforge/mcmaven/cli/Main.java +@@ -14,0 +15 @@ import java.util.ArrayList; ++import java.util.Arrays; +@@ -39,0 +41,6 @@ public class Main { ++ if ("true".equalsIgnoreCase(System.getenv("ORESPAWN_MAVENIZER_OFFLINE")) ++ && Arrays.stream(args).noneMatch("--offline"::equals)) { ++ var offlineArgs = Arrays.copyOf(args, args.length + 1); ++ offlineArgs[args.length] = "--offline"; ++ args = offlineArgs; ++ } +diff --git a/src/main/java/net/minecraftforge/mcmaven/impl/util/ProcessUtils.java b/src/main/java/net/minecraftforge/mcmaven/impl/util/ProcessUtils.java +index 9fa2888..be7f6be 100644 +--- a/src/main/java/net/minecraftforge/mcmaven/impl/util/ProcessUtils.java ++++ b/src/main/java/net/minecraftforge/mcmaven/impl/util/ProcessUtils.java +@@ -307,0 +308,2 @@ public final class ProcessUtils { ++ patchMinecraft118HolderAccessor(sourcesOutput); ++ +@@ -368,0 +371,22 @@ public final class ProcessUtils { ++ private static void patchMinecraft118HolderAccessor(File sourcesOutput) { ++ var holder = new File(sourcesOutput, "net/minecraft/core/Holder.java"); ++ if (!holder.isFile()) ++ return; ++ ++ try { ++ var source = Files.readString(holder.toPath()); ++ var record = "public static record Direct(T value) implements Holder"; ++ var accessor = "\n public T value() {\n return this.value;\n }\n"; ++ if (!source.contains(record) || !source.contains(accessor)) ++ return; ++ ++ var patched = source.replace(accessor, "\n"); ++ if (patched.contains(accessor)) ++ throw new IllegalStateException("Holder.Direct accessor compatibility patch was ambiguous"); ++ Files.writeString(holder.toPath(), patched); ++ LOGGER.info("Applied Minecraft 1.18.2 Holder.Direct javac compatibility patch"); ++ } catch (IOException e) { ++ throw new RuntimeException("Failed to patch Minecraft 1.18.2 Holder source", e); ++ } ++ } ++ diff --git a/ci-fixtures/tools/minecraft-mavenizer-0.5.21-orespawn-1.18.jar b/ci-fixtures/tools/minecraft-mavenizer-0.5.21-orespawn-1.18.jar new file mode 100644 index 0000000..f88af85 Binary files /dev/null and b/ci-fixtures/tools/minecraft-mavenizer-0.5.21-orespawn-1.18.jar differ diff --git a/docs/PORT118-STATUS.md b/docs/PORT118-STATUS.md new file mode 100644 index 0000000..a7c8241 --- /dev/null +++ b/docs/PORT118-STATUS.md @@ -0,0 +1,167 @@ +# baseSteam 1.18.2 port notes + +The release branch uses the shared Base Metals 3.1.2 migration hook and the +published basePower 2.4.10.118021 build. +See [gradle.properties](../gradle.properties) for the exact Power, Base Metals +and OreSpawn pins. + +The first locally tested Steam player jar had SHA-256 +`2E7131FD22F9486905212F64323F5E11EEFBE0A538499E92477373D0C5D2A2CC`. +The notes below record earlier port milestones and their original inputs. + +- Source: PA117 `feature/basesteam-1.17.1-identity-migration` at + `c8e3febcd11c6854d8073c9c053c7c162cdd6687`, cloned without hardlinks. +- Branch: `feature/basesteam-1.18.2-port`; candidate `2.2.11.118021`. +- Target: Forge `40.3.0`, official `1.18.2`, MCP `20220404.173914`, + Temurin `17.0.1+12`, ForgeGradle `7.0.34`, Gradle `9.6.1`. +- The original port was tested locally on disposable worlds; the notes below + describe that candidate, not the later publication build. + +## Port changes + +Blocks, items and enchantments are constructed in their own Forge registry +events. Machine GUI definitions run in queued common setup. Power's shared +creative tab, conduit identifiers, machine GUI API and migration API are kept. +Steam creates no Power fluid instance during block registration. + +Native block-entity saves now use `saveAdditional` and invoke the Power/Forge +superclass hook. Existing keys and value representations are unchanged. Drill +relocation uses full native metadata; drill-bit updates use native typed packets +and retain the direction-only packet payload. The musket uses native item tag +keys for the same blackpowder ammo and steel tags. + +Mod ID, Java packages, registry paths, migration aliases and data/asset files +are unchanged. The only changed production resources are `mods.toml` target +and dependency ranges and `pack.mcmeta` (data 9, resources 8). + +## Exact inputs + +The Base Metals and OreSpawn hashes below are local inputs. The source commits +identify the observed checkout HEADs, not independent rebuild or public-byte proof. + +| Input | Checkout HEAD | Development SHA-256 | Player SHA-256 | +| --- | --- | --- | --- | +| Base Metals 3.1.1.118021 | `5261c13744bbec2c2d394ba90bdce4ff92cc465a` | `CA896792EB3AA09264EF61068CFB6FEAA8AE3975186BE8E10C062CEB971B0354` | `0B72EFB4868CB49436B5DB07EA4606DA4B5EFDF84E510C8A48FD4264F7C110B6` | +| OreSpawn 4.1.0.118021 | `d31f52f296739f34a0c13f554abf3e1ac071ae25` | `34B25B2116FD91B0050C80393F8356A5CA256E707BF443E4882BE5CFAD1335B6` | `BD8D84A96DAFB7668E95D61B9D72C62AEF3440675473E41244C182C64653356F` | +| basePower 2.4.10.118021 | `8bd3b8213554551fc688a46eb1653a0b572b8d0a` | `CDDB8E1BB6998F625BEEF677B4E2FC87847A450D77775DA86CF31566A926D28F` | `3C3E9154BC0F7CC88C4AE4C5F0044527D7F39BBF12C71F83AD99CF2E9646E273` | + +Final Power inputs are in +`D:/PowerAdvantage/_verification/base-118-port-20261109/power-source-pin-a/`. +Earlier provisional receipts used API-only SHA-256 +`13A8E80FA768E836D7F9998533098AEB3467E56821D11EB595A097A1A068CC26`; +the final pin audit no longer accepts it. +Companion JARs were audited directly from the requested local checkouts. +The IAF118 Mavenizer fixture is checksum-verified and build-only. It corrects +the Forge 40 decompiler's redundant `Holder.Direct.value()` accessor. + +## Local receipts + +Receipts are in the clone's ignored `evidence/port118/` directory. + +- `01-companion-audit.log`: failed before module tasks on the stock Mavenizer + Forge 40 `Holder.Direct` recompile. `02-provisional-compile.log`: fixed + recompile, but cold offline injection lacked the exact Forge universal JAR. +- `javac-preflight-a.log`: old musket item-tag calls fail on 1.18.2. + `javac-preflight-test-b.log`: old fuel test tag loader fails on 1.18.2. + `junit-preflight-c.log`: standalone Forge registry freeze and old pack + assertion; retained as a failed preflight. +- `03-provisional-compile.log`: audited four companion JARs and the frozen API, + processed resources, compiled main/test Java and built a development JAR. +- `06-registry-lifecycle-red.log` and `06-test-results/`: three expected + pre-fix lifecycle failures. `07-provisional-lifecycle-green.log` and + `07-test-results/`: 206 tests across 30 suites, zero failures/errors/skips, + development JAR and Eclipse production classpath/configuration audit. +- Development JAR SHA-256 at that checkpoint: + `8345A977EA8D40CD393006C9EFA87F4BA66388BC0098510E538124DFC610871A`. + Mapped Forge development JAR SHA-256: + `6D3A11ADEEB84F2DCE591603451411F348EC9F5418D61284B3AEDEFAC00ACDF7`. +- The provisional tests used the test-only agent built inside this clone + from the unmodified Power bootstrap Git blob + `c9780d80465b3ffe5ef00752abb4a94097233843`. Agent SHA-256: + `137EFCA4B74C6905DB2ED168551BE32416CE061DF3F45A5AED2D701E1FC9DBCC`. +- `08-provisional-release-rejected.log` confirms release audits reject + the API-only input. `09-companion-variant-rejected.log` confirms the + development audit rejects the otherwise valid Base Metals player JAR. +- Source-only verification: 179 resource JSON files, 12 block-entity + factories, 17 harvest entries and 24 generated recipe unlocks checked. + +## Packaged server diagnostic + +`10-diagnostic-remap.log` built a Forge-remapped Steam intermediate, not a +release JAR. The compile input remained the frozen API-only Power JAR. +The separate Power player input was copied from its existing successful +`smoke-power-c` receipt and independently hashed before staging. + +- Steam diagnostic SHA-256: `4B381E6F548696649328C57FF2DCAC4C83E51C6D214E94BA32C0D2D04E9D4BD8`. +- Power diagnostic player SHA-256: `FE7F0EDD01F16A7FA33DD748E0395FEDE8DEC32FE77E5B27A6F13EED7D712C55`. +- With the above Base Metals and OreSpawn player JARs, a new disposable + Forge server reached `Done`. Both fresh boot and cold restart read back + `basesteam:steam_boiler_coal`, `Tank={Empty: 1b}` and the drill-bit + `dir=5b`. Both stopped cleanly with no error-level console entries. + Exact staged hashes and console receipts are in `packaged-server-a/`. +- A separate new world in APOCALYPTIC mode with no Base Metals JAR reached + `Done`, passed the same fresh state checks, saved and stopped with no + error-level console entries. OreSpawn stayed installed because Power + requires it. Exact receipts are in `packaged-server-no-basemetals-a/`. + This checks startup without the optional serializer, not crafting balance. + +## Matched Power release and reproducibility + +The full local suite passed with the exact Power source and JARs above. +Both the primary checkout and a clean source export in a path with spaces +executed 35 Gradle tasks and passed 206 JUnit tests in 30 suites, with no +failures, errors, or skips. The Eclipse production classpath and launch +configuration audit passed in both paths; Eclipse itself was not launched. + +```text +check build deobfJar javadoc verifyReleaseDependencies verifyReleaseArtifacts +writeReleaseChecksums verifyPlayerDependencies verifyEclipseProductionClasspath +``` + +Both builds used `--offline --no-daemon --no-build-cache --rerun-tasks` +and `ORESPAWN_MAVENIZER_OFFLINE=true`, with explicit Temurin 17/8/25 +toolchain paths and no automatic toolchain detection or download. The clean +export had no previous build or project cache. Both used the same warmed +dependency cache, so this is a clean-source reproducibility check, not a +cold dependency-cache build. + +- Exact source Git tree: `9fbf09d19d9324429d5dce448addda54ac478607`. + The source archive was captured before this results section was added. +- `source.zip` SHA-256: + `C86B1C2597B0B71D2B5BBD37284BE5534A2374E4E50BD3E672F00EE1EF044DEB`. + The exported Git index independently reconstructed the same tree. +- Test-only Power capability agent SHA-256: + `A71C3FB1B83DEAF13E3D40A533055DB2917B21EA3335C7D06164173EBB5B0F35`. + +All four JARs and the generated `SHA256SUMS` file matched byte for byte: + +| Frozen artifact in `evidence/port118-release-a/artifacts/` | SHA-256 | +| --- | --- | +| `baseSteam-2.2.11.118021.jar` | `C35DB2E2F1012184F2CC1EBF01032290EC520868F0A0DB24E98F9C88CFE57942` | +| `baseSteam-2.2.11.118021-deobf.jar` | `8345A977EA8D40CD393006C9EFA87F4BA66388BC0098510E538124DFC610871A` | +| `baseSteam-2.2.11.118021-sources.jar` | `525F2E149F8C64752EF6E8B77EEF7175DE42BDC7F7395B727BAB7B9776FA8F12` | +| `baseSteam-2.2.11.118021-javadoc.jar` | `718C301FD737E3DC1BC7C13D48AD70027E15D7B662E7C673C12FB07990B24C24` | +| `SHA256SUMS` | `EC071EB6709784B27699AD989DB0ED38FEE221353C30F15DC971FDE511DD02E2` | + +Primary Gradle, JUnit and source archive receipts are in +`evidence/port118-release-a/`; the independent repeat and +`comparison.json`, `byte-comparison.txt` and `test-count-comparison.json` +are in `evidence/port118-release-repro-a/`. The matched pin audit rejects the +old API-only JAR by SHA-256, as recorded in `old-api-rejected.log`. +The historical provisional and failing receipts remain in `evidence/port118/`. + +## Matched packaged server smoke + +The frozen Steam release JAR and the matched Power, Base Metals and OreSpawn +player JARs above were copied into a fresh loopback-only Forge 40.3.0 server +inside this clone. The fresh boot and cold restart passed with exit code zero +and no error-level console entries. Both runs read the same coal boiler ID, +`Tank={Empty: 1b}`, and drill-bit `dir=5b`. The staged hashes, commands, and +both receipts are in `evidence/port118-release-a/server-smoke/`. + +## Pending gates + +The combined harness, Eclipse launch execution, packaged client/GUI, +legacy-world migration, and manual gameplay acceptance are not covered +by this local release suite and narrow server smoke. No earlier version's +results qualify this candidate. diff --git a/gradle.properties b/gradle.properties index 6b28899..7c6a358 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,53 +5,54 @@ org.gradle.caching=true org.gradle.parallel=false net.minecraftforge.gradle.merge-source-sets=false -minecraft_version=1.17.1 -minecraft_version_range=[1.17.1] -forge_version=37.1.1 -forge_installer_sha256=31F2319B1B9CA491AACCC020887240F8524A4FF2A8E0F8B795E17E889CBC043B -forge_version_range=[37.1.1,38) -loader_version_range=[37,38) +minecraft_version=1.18.2 +minecraft_version_range=[1.18.2] +forge_version=40.3.0 +forge_installer_sha256=9434C29790504DCD11CE97CB30DD8891B4B18848982357BC1B5ABFA79FB95103 +forge_version_range=[40.3.0,41) +loader_version_range=[40,41) mapping_channel=official -mapping_version=1.17.1 -mcp_version=20210706.113038 +mapping_version=1.18.2 +mcp_version=20220404.173914 loader_name=forge loader_code=1 -java_version=16 -java_toolchain_version=16.0.2+7 -java_setup_version=16.0.2+7 +java_version=17 +java_toolchain_version=17.0.1+12 +java_setup_version=17.0.1+12 gradle_java_version=17 curseforge_project_id=1781373 mod_id=basesteam mod_name=baseSteam mod_license=GPL-3.0-only -mod_version=2.2.10.117011 +mod_version=2.2.11.118021 mod_group=zone.moddev.mc.basesteam mod_authors=DrCyano mod_description=Steam-powered machines and infrastructure for basePower. cf_requirements=basepower,base-metals cf_optionals= -cf_versions=1.17.1 +cf_versions=1.18.2 -poweradvantage_version=2.4.9.117011 +poweradvantage_version=2.4.10.118021 poweradvantage_source_repository=MinecraftModDevelopmentMods/basePower -poweradvantage_source_ref=bb7023835fd41aefe89c92275caa74da9e126cc7 -poweradvantage_deobf_sha256=AFC3FBDF84FF7E8FF8929F8192FDBCEC170C9AE1CF1B248E1C4B9C6ED17AA357 -# Player jar is a separate runtime input, not a development jar or publication claim. -poweradvantage_player_sha256=A788C08163CECE1C6D33FBDC295BE6EC272DE9AF7911BE74E707D0EE9121DF73 +poweradvantage_source_ref=bcf5105ef5c10ba36c26c8ff941742fecc3e11a1 +poweradvantage_deobf_sha256=767BABC0D6E1536DCB937B9F1DC565AF348B3CAF92D3207671EF1E1C8D8EF5D4 +# The player jar is the published build. The development jar is rebuilt from its source. +poweradvantage_player_sha256=8CA46D5BCBFFDDAEB4F40137B5BF71E5AF7DD36515E5BFEA751FC5C02249823B -basemetals_version=3.1.1.117011 -# Local, unpushed source. Hosted builds must be able to check out this exact commit. -basemetals_source_commit=65f9956c07d5d0c0b6ec0ac2108ce69c3ce52c6e -basemetals_deobf_sha256=83A8C32C633B56BB7E4405E8B3B24A4DEA32B9119216E5097B884056D679CAD1 +basemetals_version=3.1.2.118021 +basemetals_source_commit=3488ed58403722ac3ff203cff7680dfc16e0fd3f +basemetals_deobf_sha256=E4F9E2CDE355275218C79954D3792D000615790C7CBAE5FCEC59636A8C4CA57D +basemetals_player_sha256=5595C2CE7ADCDF1599E4327F1130121148A5CACD928CBC2190312D61F7CA37A9 -orespawn_version=4.1.0.117011 -orespawn_source_commit=b56e2b62a1a6ea4d1dd96f8646a87801a817dbdf -orespawn_deobf_sha256=CB7A736314842B110B2E4788FBD5E21D4AE8803C5ED34A49199B034BE572DA39 +orespawn_version=4.1.0.118021 +orespawn_source_commit=d31f52f296739f34a0c13f554abf3e1ac071ae25 +orespawn_deobf_sha256=34B25B2116FD91B0050C80393F8356A5CA256E707BF443E4882BE5CFAD1335B6 +orespawn_player_sha256=BD8D84A96DAFB7668E95D61B9D72C62AEF3440675473E41244C182C64653356F # Published runtime bytes are distinct from development API jars. -basemetals_public_sha256=F3053F23D2415476FC52426A24E3B0B828198A678FBD5DD789923DB2063E0FEB -orespawn_public_sha256=1BDEDCBB179CB5A3B169E5C8A6B301E3888AE15B94108B0AEC627D705CFAD833 +basemetals_public_sha256=5595C2CE7ADCDF1599E4327F1130121148A5CACD928CBC2190312D61F7CA37A9 +orespawn_public_sha256=BD8D84A96DAFB7668E95D61B9D72C62AEF3440675473E41244C182C64653356F diff --git a/src/main/java/zone/moddev/mc/basesteam/SteamAdvantage.java b/src/main/java/zone/moddev/mc/basesteam/SteamAdvantage.java index 5ef3722..567795f 100644 --- a/src/main/java/zone/moddev/mc/basesteam/SteamAdvantage.java +++ b/src/main/java/zone/moddev/mc/basesteam/SteamAdvantage.java @@ -20,11 +20,7 @@ import cyano.poweradvantage.api.migration.LegacyConfigMigration.Mapping; import cyano.poweradvantage.api.migration.LegacyConfigMigration.ValueType; import zone.moddev.mc.basesteam.config.SteamAdvantageConfig; -import zone.moddev.mc.basesteam.init.Blocks; -import zone.moddev.mc.basesteam.init.Enchantments; -import zone.moddev.mc.basesteam.init.Entities; import zone.moddev.mc.basesteam.init.GUI; -import zone.moddev.mc.basesteam.init.Items; import zone.moddev.mc.basesteam.init.Recipes; import zone.moddev.mc.basesteam.init.TreasureChests; import zone.moddev.mc.basesteam.init.Villages; @@ -32,12 +28,12 @@ import zone.moddev.mc.basesteam.migration.SteamConfigMigration; import zone.moddev.mc.basesteam.recipe.RecipeConditions; -/** Forge 1.17 entry point for baseSteam. */ +/** Forge 1.18.2 entry point for baseSteam. */ @Mod(SteamAdvantage.MODID) public final class SteamAdvantage { public static final String MODID = "basesteam"; public static final String NAME = "baseSteam"; - public static final String VERSION = "2.2.10.117011"; + public static final String VERSION = "2.2.11.118021"; public static final Logger LOGGER = LogManager.getLogger(MODID); public static float MUSKET_DAMAGE = 20.0F; @@ -47,11 +43,6 @@ public final class SteamAdvantage { public SteamAdvantage() { SteamLegacyMigrations.register(); - Blocks.init(); - Items.init(); - Entities.init(); - Enchantments.init(); - GUI.init(); migrateLegacyConfiguration(); ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, SteamAdvantageConfig.SPEC); @@ -90,6 +81,7 @@ private static void migrateLegacyConfiguration() { private void commonSetup(FMLCommonSetupEvent event) { // CraftingHelper's shared condition map is not safe during parallel loading. event.enqueueWork(RecipeConditions::register); + event.enqueueWork(GUI::init); SteamAdvantageConfig.apply(); Recipes.init(); TreasureChests.init(FMLPaths.CONFIGDIR.get()); diff --git a/src/main/java/zone/moddev/mc/basesteam/blocks/DrillBitTileEntity.java b/src/main/java/zone/moddev/mc/basesteam/blocks/DrillBitTileEntity.java index 9d29989..363a81c 100644 --- a/src/main/java/zone/moddev/mc/basesteam/blocks/DrillBitTileEntity.java +++ b/src/main/java/zone/moddev/mc/basesteam/blocks/DrillBitTileEntity.java @@ -78,10 +78,9 @@ private void collectLine(List line, Direction f){ } @Override - public CompoundTag save(CompoundTag root){ - super.save(root); + protected void saveAdditional(CompoundTag root){ + super.saveAdditional(root); writeDirectionToNBT(root); - return root; } @@ -93,7 +92,7 @@ public void load(CompoundTag root){ @Override public CompoundTag getUpdateTag() { - return save(new CompoundTag()); + return saveWithoutMetadata(); } /** @@ -103,7 +102,7 @@ public CompoundTag getUpdateTag() { public ClientboundBlockEntityDataPacket getUpdatePacket(){ CompoundTag nbtTag = new CompoundTag(); writeDirectionToNBT(nbtTag); - return new ClientboundBlockEntityDataPacket(this.getBlockPos(), 0, nbtTag); + return ClientboundBlockEntityDataPacket.create(this, tile -> nbtTag); } /** * Receives the network packet made by getDescriptionPacket() diff --git a/src/main/java/zone/moddev/mc/basesteam/init/Blocks.java b/src/main/java/zone/moddev/mc/basesteam/init/Blocks.java index ec84c9f..6acd434 100644 --- a/src/main/java/zone/moddev/mc/basesteam/init/Blocks.java +++ b/src/main/java/zone/moddev/mc/basesteam/init/Blocks.java @@ -57,6 +57,7 @@ public final class Blocks { private static boolean initialized; private Blocks() {} + /** Constructs blocks during the Forge block registry event. */ public static synchronized void init() { if (initialized) return; ItemGroups.init(); @@ -98,7 +99,6 @@ public static void registerBlocks(RegistryEvent.Register event) { @SubscribeEvent public static void registerBlockItems(RegistryEvent.Register event) { - init(); for (Map.Entry entry : ALL_BLOCKS.entrySet()) { Item.Properties properties = new Item.Properties(); if (!"pump_pipe_steam".equals(entry.getKey()) && !"drillbit".equals(entry.getKey())) { diff --git a/src/main/java/zone/moddev/mc/basesteam/init/Enchantments.java b/src/main/java/zone/moddev/mc/basesteam/init/Enchantments.java index 6e08b32..e631bf5 100644 --- a/src/main/java/zone/moddev/mc/basesteam/init/Enchantments.java +++ b/src/main/java/zone/moddev/mc/basesteam/init/Enchantments.java @@ -25,7 +25,7 @@ public static void init(){ @SubscribeEvent public static void registerEnchantments(RegistryEvent.Register event) { - createEnchantments(); + init(); event.getRegistry().registerAll(high_explosive, powderless, rapid_reload, recoil); } diff --git a/src/main/java/zone/moddev/mc/basesteam/init/GUI.java b/src/main/java/zone/moddev/mc/basesteam/init/GUI.java index 7699ef5..4ca50e1 100644 --- a/src/main/java/zone/moddev/mc/basesteam/init/GUI.java +++ b/src/main/java/zone/moddev/mc/basesteam/init/GUI.java @@ -10,8 +10,6 @@ public class GUI { public static void init(){ if(initDone) return; - Blocks.init(); - Entities.init(); Blocks.steam_boiler_coal.setGuiID(MachineGUIRegistry.addGUI(new CoalBoilerGUI()),PowerAdvantage.getInstance()); Blocks.steam_tank.setGuiID(MachineGUIRegistry.addGUI(new SteamTankGUI()),PowerAdvantage.getInstance()); diff --git a/src/main/java/zone/moddev/mc/basesteam/init/Items.java b/src/main/java/zone/moddev/mc/basesteam/init/Items.java index 1a2ab28..2b0180f 100644 --- a/src/main/java/zone/moddev/mc/basesteam/init/Items.java +++ b/src/main/java/zone/moddev/mc/basesteam/init/Items.java @@ -23,9 +23,10 @@ public final class Items { private Items() {} + /** Constructs items during the Forge item registry event. */ public static synchronized void init() { if (initialized) return; - Blocks.init(); + ItemGroups.init(); steam_governor = add("steam_governor", new Item(properties())); steam_drill_bit = add("steam_drill_bit", new Item(properties())); blackpowder_cartridge = add("blackpowder_cartridge", new Item(properties())); diff --git a/src/main/java/zone/moddev/mc/basesteam/init/Recipes.java b/src/main/java/zone/moddev/mc/basesteam/init/Recipes.java index 838a313..9440183 100644 --- a/src/main/java/zone/moddev/mc/basesteam/init/Recipes.java +++ b/src/main/java/zone/moddev/mc/basesteam/init/Recipes.java @@ -12,8 +12,6 @@ public class Recipes { public static void init(){ if(initDone) return; - Blocks.init(); - Items.init(); diff --git a/src/main/java/zone/moddev/mc/basesteam/items/MusketItem.java b/src/main/java/zone/moddev/mc/basesteam/items/MusketItem.java index 9963874..0929ceb 100644 --- a/src/main/java/zone/moddev/mc/basesteam/items/MusketItem.java +++ b/src/main/java/zone/moddev/mc/basesteam/items/MusketItem.java @@ -53,6 +53,7 @@ import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraft.tags.ItemTags; +import net.minecraft.tags.TagKey; @SuppressWarnings("deprecation") public class MusketItem extends net.minecraft.world.item.Item{ @@ -62,8 +63,8 @@ public class MusketItem extends net.minecraft.world.item.Item{ public static final double MAX_RANGE = 64; private static final int maxUseTime = 7200; - private static final ResourceLocation BLACKPOWDER_AMMO_TAG = new ResourceLocation("forge", "ammo/blackpowder"); - private static final ResourceLocation STEEL_INGOT_TAG = new ResourceLocation("forge", "ingots/steel"); + private static final TagKey BLACKPOWDER_AMMO_TAG = ItemTags.create(new ResourceLocation("forge", "ammo/blackpowder")); + private static final TagKey STEEL_INGOT_TAG = ItemTags.create(new ResourceLocation("forge", "ingots/steel")); public MusketItem(Item.Properties properties){ super(properties.defaultDurability(250)); @@ -324,7 +325,7 @@ public static boolean hasAmmo(LivingEntity e, ItemStack musket) { for (int i = 0; i < playerEntity.getInventory().items.size(); i++) { ItemStack inventoryStack = playerEntity.getInventory().items.get(i); if (inventoryStack.isEmpty()) continue; - if (ItemTags.getAllTags().getTagOrEmpty(BLACKPOWDER_AMMO_TAG).contains(inventoryStack.getItem())) return true; + if (inventoryStack.is(BLACKPOWDER_AMMO_TAG)) return true; } } return false; @@ -340,7 +341,7 @@ public static void decrementAmmo(LivingEntity e, ItemStack musket) { for (int i = 0; i < playerEntity.getInventory().items.size(); i++) { ItemStack inventoryStack = playerEntity.getInventory().items.get(i); if (inventoryStack.isEmpty()) continue; - if (ItemTags.getAllTags().getTagOrEmpty(BLACKPOWDER_AMMO_TAG).contains(inventoryStack.getItem())) { + if (inventoryStack.is(BLACKPOWDER_AMMO_TAG)) { inventoryStack.shrink(1); if (inventoryStack.isEmpty()) playerEntity.getInventory().items.set(i, ItemStack.EMPTY); return; @@ -439,7 +440,7 @@ private static double min(double a, double b){ @Override public boolean isValidRepairItem(ItemStack srcItemStack, ItemStack rawMaterial) { // repair with steel ingots - return ItemTags.getAllTags().getTagOrEmpty(STEEL_INGOT_TAG).contains(rawMaterial.getItem()); + return rawMaterial.is(STEEL_INGOT_TAG); } @Override diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/BlastFurnaceTileEntity.java b/src/main/java/zone/moddev/mc/basesteam/machines/BlastFurnaceTileEntity.java index 8b5ae96..c5d8f2f 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/BlastFurnaceTileEntity.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/BlastFurnaceTileEntity.java @@ -269,11 +269,10 @@ public void onDataFieldUpdate() { @Override - public CompoundTag save(CompoundTag tagRoot){ - super.save(tagRoot); + protected void saveAdditional(CompoundTag tagRoot){ + super.saveAdditional(tagRoot); prepareDataFieldsForSync(); tagRoot.putIntArray("Data", getDataFieldArray()); - return tagRoot; } @Override diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/CoalBoilerTileEntity.java b/src/main/java/zone/moddev/mc/basesteam/machines/CoalBoilerTileEntity.java index be5bad7..c39e207 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/CoalBoilerTileEntity.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/CoalBoilerTileEntity.java @@ -214,14 +214,13 @@ public void onDataFieldUpdate() { * @param tagRoot An NBT tag */ @Override - public CompoundTag save(final CompoundTag tagRoot) { - super.save(tagRoot); + protected void saveAdditional(final CompoundTag tagRoot) { + super.saveAdditional(tagRoot); CompoundTag tankTag = new CompoundTag(); this.getTank().writeToNBT(tankTag); tagRoot.put("Tank", tankTag); if(this.burnTime > 0)tagRoot.putInt("BurnTime", this.burnTime); if(this.totalBurnTime > 0)tagRoot.putInt("BurnTimeTotal", this.totalBurnTime); - return tagRoot; } /** * Handles data saving and loading diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/ElectricBoilerTileEntity.java b/src/main/java/zone/moddev/mc/basesteam/machines/ElectricBoilerTileEntity.java index a3ec491..a976e1f 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/ElectricBoilerTileEntity.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/ElectricBoilerTileEntity.java @@ -187,13 +187,12 @@ public void onDataFieldUpdate() { * @param tagRoot An NBT tag */ @Override - public CompoundTag save(final CompoundTag tagRoot) { - super.save(tagRoot); + protected void saveAdditional(final CompoundTag tagRoot) { + super.saveAdditional(tagRoot); CompoundTag tankTag = new CompoundTag(); this.getTank().writeToNBT(tankTag); tagRoot.put("Tank", tankTag); if(getEnergy(ELECTRIC_POWER) > 0)tagRoot.putFloat("Electricity", getEnergy(ELECTRIC_POWER)); - return tagRoot; } /** * Handles data saving and loading diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/GeothermalBoilerTileEntity.java b/src/main/java/zone/moddev/mc/basesteam/machines/GeothermalBoilerTileEntity.java index 46d8581..b34abd6 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/GeothermalBoilerTileEntity.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/GeothermalBoilerTileEntity.java @@ -220,13 +220,12 @@ public void onDataFieldUpdate() { * @param tagRoot An NBT tag */ @Override - public CompoundTag save(final CompoundTag tagRoot) { - super.save(tagRoot); + protected void saveAdditional(final CompoundTag tagRoot) { + super.saveAdditional(tagRoot); CompoundTag tankTag = new CompoundTag(); this.getTank().writeToNBT(tankTag); tagRoot.put("Tank", tankTag); if(this.temperature > 0)tagRoot.putFloat("Temperature", this.temperature); - return tagRoot; } /** * Handles data saving and loading diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/OilBoilerTileEntity.java b/src/main/java/zone/moddev/mc/basesteam/machines/OilBoilerTileEntity.java index 7affee0..02aba53 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/OilBoilerTileEntity.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/OilBoilerTileEntity.java @@ -264,8 +264,8 @@ public void onDataFieldUpdate() { * @param tagRoot An NBT tag */ @Override - public CompoundTag save(final CompoundTag tagRoot) { - super.save(tagRoot); + protected void saveAdditional(final CompoundTag tagRoot) { + super.saveAdditional(tagRoot); CompoundTag tankTag = new CompoundTag(); this.getWaterTank().writeToNBT(tankTag); tagRoot.put("WaterTank", tankTag); @@ -274,7 +274,6 @@ public CompoundTag save(final CompoundTag tagRoot) { tagRoot.put("FuelTank", tankTag2); if(this.burnTime > 0)tagRoot.putInt("BurnTime", this.burnTime); if(this.totalBurnTime > 0)tagRoot.putInt("BurnTimeTotal", this.totalBurnTime); - return tagRoot; } /** * Handles data saving and loading diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/RockCrusherTileEntity.java b/src/main/java/zone/moddev/mc/basesteam/machines/RockCrusherTileEntity.java index a7304d4..6e41f55 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/RockCrusherTileEntity.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/RockCrusherTileEntity.java @@ -199,10 +199,9 @@ private int canAddToOutputInventory(ItemStack output) { } @Override - public CompoundTag save(CompoundTag tagRoot){ - super.save(tagRoot); + protected void saveAdditional(CompoundTag tagRoot){ + super.saveAdditional(tagRoot); tagRoot.putShort("progress",(short)progress); - return tagRoot; } @Override diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/SteamDrillTileEntity.java b/src/main/java/zone/moddev/mc/basesteam/machines/SteamDrillTileEntity.java index c2de70e..c9698fc 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/SteamDrillTileEntity.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/SteamDrillTileEntity.java @@ -189,8 +189,7 @@ private boolean followTrack(){ return false; } SteamDrillTileEntity te = (SteamDrillTileEntity)movedTile; - CompoundTag dataTransfer = new CompoundTag(); - this.save(dataTransfer); + CompoundTag dataTransfer = saveWithFullMetadata(); dataTransfer.putInt("x", nextPos.getX()); dataTransfer.putInt("y", nextPos.getY()); dataTransfer.putInt("z", nextPos.getZ()); @@ -470,8 +469,8 @@ private void inventoryTransfer(BlockPos adj, Direction otherFace) { } @Override - public CompoundTag save(CompoundTag tagRoot){ - super.save(tagRoot); + protected void saveAdditional(CompoundTag tagRoot){ + super.saveAdditional(tagRoot); tagRoot.putShort("progress",(short)progress); // getFacing refreshes this during normal ticks. World lookups here can // synchronously reload a chunk while its unload-save is in progress. @@ -482,7 +481,6 @@ public CompoundTag save(CompoundTag tagRoot){ tagRoot.putInt("targetZ", targetBlockCoord.getZ()); if(savedTargetState != null) tagRoot.put("TargetState", savedTargetState.copy()); } - return tagRoot; } @Override diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/SteamElevatorTileEntity.java b/src/main/java/zone/moddev/mc/basesteam/machines/SteamElevatorTileEntity.java index cfee4fb..f92ef8f 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/SteamElevatorTileEntity.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/SteamElevatorTileEntity.java @@ -172,10 +172,9 @@ public void onDataFieldUpdate() { @Override - public CompoundTag save(CompoundTag tagRoot){ - super.save(tagRoot); + protected void saveAdditional(CompoundTag tagRoot){ + super.saveAdditional(tagRoot); tagRoot.putBoolean("up",above); - return tagRoot; } @Override diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/SteamPumpTileEntity.java b/src/main/java/zone/moddev/mc/basesteam/machines/SteamPumpTileEntity.java index f8316fa..452c076 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/SteamPumpTileEntity.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/SteamPumpTileEntity.java @@ -284,13 +284,12 @@ public void onDataFieldUpdate() { * @param tagRoot An NBT tag */ @Override - public CompoundTag save(final CompoundTag tagRoot) { - super.save(tagRoot); + protected void saveAdditional(final CompoundTag tagRoot) { + super.saveAdditional(tagRoot); CompoundTag tankTag = new CompoundTag(); this.getTank().writeToNBT(tankTag); tagRoot.put("Tank", tankTag); tagRoot.putByte("NextPump", (byte)this.timeUntilNextPump); - return tagRoot; } /** * Handles data saving and loading diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/SteamStillTileEntity.java b/src/main/java/zone/moddev/mc/basesteam/machines/SteamStillTileEntity.java index 738143b..968223c 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/SteamStillTileEntity.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/SteamStillTileEntity.java @@ -176,15 +176,14 @@ public void onDataFieldUpdate() { * @param tagRoot An NBT tag */ @Override - public CompoundTag save(final CompoundTag tagRoot) { - super.save(tagRoot); + protected void saveAdditional(final CompoundTag tagRoot) { + super.saveAdditional(tagRoot); CompoundTag tankTag = new CompoundTag(); this.getOutputTank().writeToNBT(tankTag); tagRoot.put("TankOut", tankTag); CompoundTag tankTag2 = new CompoundTag(); this.getInputTank().writeToNBT(tankTag2); tagRoot.put("TankIn", tankTag2); - return tagRoot; } /** * Handles data saving and loading diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 28891ea..e03ba03 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -14,14 +14,14 @@ description='''Steam-powered machines and infrastructure for basePower.''' [[dependencies.basesteam]] modId="basepower" mandatory=true -versionRange="[2.4.9.117011,3.0.0)" +versionRange="[2.4.10.118021,3.0.0)" ordering="AFTER" side="BOTH" [[dependencies.basesteam]] modId="basemetals" mandatory=false -versionRange="[3.1.1.117011,4.0.0)" +versionRange="[3.1.2.118021,4.0.0)" ordering="AFTER" side="BOTH" diff --git a/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta index 776bb89..9ecce85 100644 --- a/src/main/resources/pack.mcmeta +++ b/src/main/resources/pack.mcmeta @@ -1,6 +1,8 @@ { "pack":{ - "pack_format":7, + "pack_format":9, + "forge:resource_pack_format":8, + "forge:data_pack_format":9, "description":"baseSteam" } } diff --git a/src/test/java/zone/moddev/mc/basesteam/blocks/DrillBitPersistenceTest.java b/src/test/java/zone/moddev/mc/basesteam/blocks/DrillBitPersistenceTest.java index cef319c..45dba4a 100644 --- a/src/test/java/zone/moddev/mc/basesteam/blocks/DrillBitPersistenceTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/blocks/DrillBitPersistenceTest.java @@ -1,5 +1,6 @@ package zone.moddev.mc.basesteam.blocks; +import zone.moddev.mc.basesteam.testing.NativeTestBootstrap; import net.minecraft.nbt.CompoundTag; import net.minecraft.core.Direction; import org.junit.Test; @@ -7,7 +8,7 @@ public class DrillBitPersistenceTest { @org.junit.BeforeClass public static void bootstrapNativeRegistries() { - net.minecraft.SharedConstants.tryDetectVersion(); net.minecraft.server.Bootstrap.bootStrap(); + NativeTestBootstrap.start(); } @Test public void allSixDirectionsSurviveNativeUpdatePackets() { @@ -23,6 +24,23 @@ public class DrillBitPersistenceTest { } } + @Test public void allSixDirectionsSurviveNativeSaveAndChunkUpdateTag() { + for (Direction direction : Direction.values()) { + DrillBitTileEntity tile = new DrillBitTileEntity(); + tile.setDirection(direction); + + CompoundTag saved = tile.saveWithoutMetadata(); + assertEquals(direction.get3DDataValue(), saved.getByte("dir")); + DrillBitTileEntity restored = new DrillBitTileEntity(); + restored.load(saved); + assertEquals(direction.get3DDataValue(), restored.getUpdateTag().getByte("dir")); + + DrillBitTileEntity client = new DrillBitTileEntity(); + client.handleUpdateTag(tile.getUpdateTag()); + assertEquals(direction.get3DDataValue(), client.getUpdatePacket().getTag().getByte("dir")); + } + } + @Test public void oldDirectionKeyRemainsReadableAndCanonicalKeyWins() { CompoundTag tag = new CompoundTag(); tag.putByte("d", (byte) Direction.EAST.get3DDataValue()); diff --git a/src/test/java/zone/moddev/mc/basesteam/items/MusketPersistenceTest.java b/src/test/java/zone/moddev/mc/basesteam/items/MusketPersistenceTest.java index 4562c03..46a890d 100644 --- a/src/test/java/zone/moddev/mc/basesteam/items/MusketPersistenceTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/items/MusketPersistenceTest.java @@ -1,5 +1,6 @@ package zone.moddev.mc.basesteam.items; +import zone.moddev.mc.basesteam.testing.NativeTestBootstrap; import net.minecraft.world.item.ItemStack; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.phys.AABB; @@ -10,7 +11,7 @@ public class MusketPersistenceTest { @BeforeClass public static void bootstrapNativeRegistries() { - net.minecraft.SharedConstants.tryDetectVersion(); net.minecraft.server.Bootstrap.bootStrap(); + NativeTestBootstrap.start(); } @Test public void loadingCycleKeepsLegacyByteValuesAndOtherNbt() { diff --git a/src/test/java/zone/moddev/mc/basesteam/items/MusketTargetingContractTest.java b/src/test/java/zone/moddev/mc/basesteam/items/MusketTargetingContractTest.java index 621ae07..d88323d 100644 --- a/src/test/java/zone/moddev/mc/basesteam/items/MusketTargetingContractTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/items/MusketTargetingContractTest.java @@ -1,5 +1,6 @@ package zone.moddev.mc.basesteam.items; +import zone.moddev.mc.basesteam.testing.NativeTestBootstrap; import java.io.InputStream; import java.util.Optional; import net.minecraft.core.Direction; @@ -16,7 +17,7 @@ public class MusketTargetingContractTest { @BeforeClass public static void bootstrap() { - net.minecraft.SharedConstants.tryDetectVersion(); net.minecraft.server.Bootstrap.bootStrap(); + NativeTestBootstrap.start(); } @Test public void nearerTerrainRejectsTargetsBehindItOnEveryAxis() { diff --git a/src/test/java/zone/moddev/mc/basesteam/machines/CoalBoilerFillPersistenceTest.java b/src/test/java/zone/moddev/mc/basesteam/machines/CoalBoilerFillPersistenceTest.java index 577d75a..5efb20e 100644 --- a/src/test/java/zone/moddev/mc/basesteam/machines/CoalBoilerFillPersistenceTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/machines/CoalBoilerFillPersistenceTest.java @@ -1,5 +1,6 @@ package zone.moddev.mc.basesteam.machines; +import zone.moddev.mc.basesteam.testing.NativeTestBootstrap; import static org.junit.Assert.*; import cyano.poweradvantage.api.ConduitType; @@ -13,7 +14,7 @@ /** Save-notification unit witness; actual disk persistence is checked by the native restart rig. */ public class CoalBoilerFillPersistenceTest { @BeforeClass public static void bootstrap() { - net.minecraft.SharedConstants.tryDetectVersion(); net.minecraft.server.Bootstrap.bootStrap(); + NativeTestBootstrap.start(); if (zone.moddev.mc.basesteam.init.Entities.STEAM_BOILER_COAL == null) zone.moddev.mc.basesteam.init.Entities.STEAM_BOILER_COAL = net.minecraft.world.level.block.entity.BlockEntityType.Builder.of(CoalBoilerTileEntity::new, net.minecraft.world.level.block.Blocks.STONE).build(null); } diff --git a/src/test/java/zone/moddev/mc/basesteam/machines/GeothermalNativeTemperatureTest.java b/src/test/java/zone/moddev/mc/basesteam/machines/GeothermalNativeTemperatureTest.java index 74ae745..8146052 100644 --- a/src/test/java/zone/moddev/mc/basesteam/machines/GeothermalNativeTemperatureTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/machines/GeothermalNativeTemperatureTest.java @@ -1,5 +1,6 @@ package zone.moddev.mc.basesteam.machines; +import zone.moddev.mc.basesteam.testing.NativeTestBootstrap; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.material.Fluids; @@ -8,7 +9,7 @@ import static org.junit.Assert.*; public class GeothermalNativeTemperatureTest { - @BeforeClass public static void bootstrap() { net.minecraft.SharedConstants.tryDetectVersion(); net.minecraft.server.Bootstrap.bootStrap(); } + @BeforeClass public static void bootstrap() { NativeTestBootstrap.start(); } @Test public void nativeLavaTemperaturePreservesTheExistingHeatInput() { assertEquals(1300, Fluids.LAVA.getAttributes().getTemperature()); diff --git a/src/test/java/zone/moddev/mc/basesteam/machines/RockCrusherComparatorTest.java b/src/test/java/zone/moddev/mc/basesteam/machines/RockCrusherComparatorTest.java index db3f983..3decaf2 100644 --- a/src/test/java/zone/moddev/mc/basesteam/machines/RockCrusherComparatorTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/machines/RockCrusherComparatorTest.java @@ -1,5 +1,6 @@ package zone.moddev.mc.basesteam.machines; +import zone.moddev.mc.basesteam.testing.NativeTestBootstrap; import static org.junit.Assert.assertEquals; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; @@ -8,7 +9,7 @@ public class RockCrusherComparatorTest { @BeforeClass public static void bootstrap() { - net.minecraft.SharedConstants.tryDetectVersion(); net.minecraft.server.Bootstrap.bootStrap(); + NativeTestBootstrap.start(); if (zone.moddev.mc.basesteam.init.Entities.STEAM_CRUSHER == null) zone.moddev.mc.basesteam.init.Entities.STEAM_CRUSHER = net.minecraft.world.level.block.entity.BlockEntityType.Builder.of(RockCrusherTileEntity::new, net.minecraft.world.level.block.Blocks.STONE).build(null); } diff --git a/src/test/java/zone/moddev/mc/basesteam/machines/RockCrusherReloadRedstoneTest.java b/src/test/java/zone/moddev/mc/basesteam/machines/RockCrusherReloadRedstoneTest.java index a881395..5fbc624 100644 --- a/src/test/java/zone/moddev/mc/basesteam/machines/RockCrusherReloadRedstoneTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/machines/RockCrusherReloadRedstoneTest.java @@ -1,5 +1,6 @@ package zone.moddev.mc.basesteam.machines; +import zone.moddev.mc.basesteam.testing.NativeTestBootstrap; import static org.junit.Assert.*; import java.lang.reflect.Field; import net.minecraft.core.BlockPos; @@ -10,7 +11,7 @@ public class RockCrusherReloadRedstoneTest { @BeforeClass public static void bootstrap() { - net.minecraft.SharedConstants.tryDetectVersion(); net.minecraft.server.Bootstrap.bootStrap(); + NativeTestBootstrap.start(); if (zone.moddev.mc.basesteam.init.Entities.STEAM_CRUSHER == null) zone.moddev.mc.basesteam.init.Entities.STEAM_CRUSHER = net.minecraft.world.level.block.entity.BlockEntityType.Builder.of(RockCrusherTileEntity::new, net.minecraft.world.level.block.Blocks.STONE).build(null); if (zone.moddev.mc.basesteam.init.Entities.STEAM_FURNACE == null) diff --git a/src/test/java/zone/moddev/mc/basesteam/machines/SteamDrillPersistenceTest.java b/src/test/java/zone/moddev/mc/basesteam/machines/SteamDrillPersistenceTest.java index aa31e89..d5ad3fc 100644 --- a/src/test/java/zone/moddev/mc/basesteam/machines/SteamDrillPersistenceTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/machines/SteamDrillPersistenceTest.java @@ -1,5 +1,6 @@ package zone.moddev.mc.basesteam.machines; +import zone.moddev.mc.basesteam.testing.NativeTestBootstrap; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.world.item.ItemStack; @@ -25,7 +26,7 @@ public class SteamDrillPersistenceTest { @BeforeClass public static void bootstrapNativeRegistries() { - net.minecraft.SharedConstants.tryDetectVersion(); net.minecraft.server.Bootstrap.bootStrap(); + NativeTestBootstrap.start(); } @Test public void retainsLegacyProgressUntilWorldIsAttached() { @@ -98,7 +99,7 @@ public class SteamDrillPersistenceTest { } int methods = 0, cachedFacingReads = 0; for (MethodNode method : node.methods) { - if (!method.name.equals("save")) continue; + if (!method.name.equals("saveAdditional")) continue; methods++; for (AbstractInsnNode instruction : method.instructions.toArray()) { if (instruction instanceof FieldInsnNode diff --git a/src/test/java/zone/moddev/mc/basesteam/machines/SteamMachinePersistenceTest.java b/src/test/java/zone/moddev/mc/basesteam/machines/SteamMachinePersistenceTest.java index 2e2c4b4..68b2c28 100644 --- a/src/test/java/zone/moddev/mc/basesteam/machines/SteamMachinePersistenceTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/machines/SteamMachinePersistenceTest.java @@ -1,5 +1,6 @@ package zone.moddev.mc.basesteam.machines; +import zone.moddev.mc.basesteam.testing.NativeTestBootstrap; import cyano.poweradvantage.api.fluid.FluidStack; import net.minecraft.world.level.block.Blocks; import net.minecraft.nbt.CompoundTag; @@ -16,7 +17,7 @@ public class SteamMachinePersistenceTest { @BeforeClass public static void bootstrapNativeTypes() { - net.minecraft.SharedConstants.tryDetectVersion(); net.minecraft.server.Bootstrap.bootStrap(); + NativeTestBootstrap.start(); Entities.STEAM_BOILER_COAL = type("steam_boiler_coal", CoalBoilerTileEntity::new); Entities.STEAM_BOILER_ELECTRIC = type("steam_boiler_electric", ElectricBoilerTileEntity::new); Entities.STEAM_BOILER_GEOTHERMAL = type("steam_boiler_geothermal", GeothermalBoilerTileEntity::new); @@ -57,7 +58,7 @@ public class SteamMachinePersistenceTest { tag.putInt("BurnTimeTotal", 1600); CoalBoilerTileEntity coal = new CoalBoilerTileEntity(); coal.load(tag); - CompoundTag saved = coal.save(new CompoundTag()); + CompoundTag saved = coal.saveWithFullMetadata(); assertEquals("basesteam:steam_boiler_coal", saved.getString("id")); assertEquals(127, saved.getInt("BurnTime")); assertEquals(1600, saved.getInt("BurnTimeTotal")); @@ -69,7 +70,7 @@ public class SteamMachinePersistenceTest { ElectricBoilerTileEntity electric = new ElectricBoilerTileEntity(); electric.load(tag); ElectricBoilerTileEntity electricRestored = new ElectricBoilerTileEntity(); - electricRestored.load(electric.save(new CompoundTag())); + electricRestored.load(electric.saveWithoutMetadata()); assertEquals(37.5F, electricRestored.getEnergy(Power.steam_power), 0.0F); assertEquals(60.0F, electricRestored.getEnergy(ElectricBoilerTileEntity.ELECTRIC_POWER), 0.0F); assertEquals(1500, electricRestored.getTank().getFluidAmount()); @@ -81,7 +82,7 @@ public class SteamMachinePersistenceTest { tag.putByte("NextPump", (byte) 11); SteamPumpTileEntity pump = new SteamPumpTileEntity(); pump.load(tag); - CompoundTag saved = pump.save(new CompoundTag()); + CompoundTag saved = pump.saveWithoutMetadata(); assertEquals(750, saved.getCompound("Tank").getInt("Amount")); assertEquals(11, saved.getByte("NextPump")); SteamPumpTileEntity pumpRestored = new SteamPumpTileEntity(); @@ -91,10 +92,10 @@ public class SteamMachinePersistenceTest { tag.putFloat("Tempoerature", 412.5F); GeothermalBoilerTileEntity geothermal = new GeothermalBoilerTileEntity(); geothermal.load(tag); - assertEquals(412.5F, geothermal.save(new CompoundTag()).getFloat("Temperature"), 0.0F); + assertEquals(412.5F, geothermal.saveWithoutMetadata().getFloat("Temperature"), 0.0F); tag.putFloat("Temperature", 500.0F); geothermal.load(tag); - assertEquals(500.0F, geothermal.save(new CompoundTag()).getFloat("Temperature"), 0.0F); + assertEquals(500.0F, geothermal.saveWithoutMetadata().getFloat("Temperature"), 0.0F); } @Test public void stillKeepsSeparateTanksAndExplicitlyEmptyReloads() { @@ -104,7 +105,7 @@ public class SteamMachinePersistenceTest { SteamStillTileEntity still = new SteamStillTileEntity(); still.load(tag); SteamStillTileEntity restored = new SteamStillTileEntity(); - restored.load(still.save(new CompoundTag())); + restored.load(still.saveWithoutMetadata()); assertEquals(625, restored.getInputTank().getFluidAmount()); assertEquals(375, restored.getOutputTank().getFluidAmount()); assertEquals(37.5F, restored.getEnergy(Power.steam_power), 0.0F); diff --git a/src/test/java/zone/moddev/mc/basesteam/machines/SteamOperationalPumpSourceTest.java b/src/test/java/zone/moddev/mc/basesteam/machines/SteamOperationalPumpSourceTest.java index a0a4148..f366d43 100644 --- a/src/test/java/zone/moddev/mc/basesteam/machines/SteamOperationalPumpSourceTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/machines/SteamOperationalPumpSourceTest.java @@ -1,5 +1,6 @@ package zone.moddev.mc.basesteam.machines; +import zone.moddev.mc.basesteam.testing.NativeTestBootstrap; import java.io.InputStream; import java.lang.reflect.Proxy; import net.minecraft.world.level.block.Block; @@ -29,7 +30,7 @@ public class SteamOperationalPumpSourceTest { private static final BlockPos SOURCE = new BlockPos(8, 80, 8); @BeforeClass public static void bootstrap() { - net.minecraft.SharedConstants.tryDetectVersion(); net.minecraft.server.Bootstrap.bootStrap(); + NativeTestBootstrap.start(); } @Test public void nativePickupRetainsTheSlabAndOnlyClearsItsWater() { diff --git a/src/test/java/zone/moddev/mc/basesteam/machines/SteamOperationalStillCapacityTest.java b/src/test/java/zone/moddev/mc/basesteam/machines/SteamOperationalStillCapacityTest.java index 5a5117f..aeb169c 100644 --- a/src/test/java/zone/moddev/mc/basesteam/machines/SteamOperationalStillCapacityTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/machines/SteamOperationalStillCapacityTest.java @@ -1,5 +1,6 @@ package zone.moddev.mc.basesteam.machines; +import zone.moddev.mc.basesteam.testing.NativeTestBootstrap; import cyano.poweradvantage.api.fluid.FluidStack; import java.lang.reflect.Method; import net.minecraft.world.level.block.Blocks; @@ -15,7 +16,7 @@ /** Pure capacity planning; native operating regressions live in SteamOperationalCases. */ public class SteamOperationalStillCapacityTest { @BeforeClass public static void bootstrap() { - net.minecraft.SharedConstants.tryDetectVersion(); net.minecraft.server.Bootstrap.bootStrap(); + NativeTestBootstrap.start(); if (Entities.STEAM_STILL == null) { Entities.STEAM_STILL = BlockEntityType.Builder.of(SteamStillTileEntity::new, Blocks.STONE).build(null); } diff --git a/src/test/java/zone/moddev/mc/basesteam/machines/WaterBoilerFluidBoundaryTest.java b/src/test/java/zone/moddev/mc/basesteam/machines/WaterBoilerFluidBoundaryTest.java index ba783ac..4a08a86 100644 --- a/src/test/java/zone/moddev/mc/basesteam/machines/WaterBoilerFluidBoundaryTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/machines/WaterBoilerFluidBoundaryTest.java @@ -1,5 +1,6 @@ package zone.moddev.mc.basesteam.machines; +import zone.moddev.mc.basesteam.testing.NativeTestBootstrap; import static org.junit.Assert.*; import java.lang.reflect.Method; import java.util.*; @@ -25,7 +26,7 @@ public static Collection boilers() { private FluidTank tank; public WaterBoilerFluidBoundaryTest(Class type) { this.type = type; } @BeforeClass public static void bootstrap() { - net.minecraft.SharedConstants.tryDetectVersion(); net.minecraft.server.Bootstrap.bootStrap(); + NativeTestBootstrap.start(); if (zone.moddev.mc.basesteam.init.Entities.STEAM_BOILER_COAL == null) zone.moddev.mc.basesteam.init.Entities.STEAM_BOILER_COAL = net.minecraft.world.level.block.entity.BlockEntityType.Builder.of(CoalBoilerTileEntity::new, net.minecraft.world.level.block.Blocks.STONE).build(null); if (zone.moddev.mc.basesteam.init.Entities.STEAM_BOILER_ELECTRIC == null) diff --git a/src/test/java/zone/moddev/mc/basesteam/machines/WaterBoilerTankNotificationTest.java b/src/test/java/zone/moddev/mc/basesteam/machines/WaterBoilerTankNotificationTest.java index 3b7e066..b37a83c 100644 --- a/src/test/java/zone/moddev/mc/basesteam/machines/WaterBoilerTankNotificationTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/machines/WaterBoilerTankNotificationTest.java @@ -1,5 +1,6 @@ package zone.moddev.mc.basesteam.machines; +import zone.moddev.mc.basesteam.testing.NativeTestBootstrap; import static org.junit.Assert.*; import cyano.poweradvantage.api.IPowerMachine; import zone.moddev.mc.basepower.init.Fluids; @@ -23,7 +24,7 @@ public class WaterBoilerTankNotificationTest { private Probe probe; public WaterBoilerTankNotificationTest(String type) { this.type = type; } @BeforeClass public static void bootstrap() { - net.minecraft.SharedConstants.tryDetectVersion(); net.minecraft.server.Bootstrap.bootStrap(); + NativeTestBootstrap.start(); if (zone.moddev.mc.basesteam.init.Entities.STEAM_BOILER_ELECTRIC == null) zone.moddev.mc.basesteam.init.Entities.STEAM_BOILER_ELECTRIC = net.minecraft.world.level.block.entity.BlockEntityType.Builder.of(ElectricBoilerTileEntity::new, net.minecraft.world.level.block.Blocks.STONE).build(null); if (zone.moddev.mc.basesteam.init.Entities.STEAM_BOILER_GEOTHERMAL == null) diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/BuildWorkflowContractTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/BuildWorkflowContractTest.java index f84caa6..159a505 100644 --- a/src/test/java/zone/moddev/mc/basesteam/resources/BuildWorkflowContractTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/resources/BuildWorkflowContractTest.java @@ -13,7 +13,9 @@ public class BuildWorkflowContractTest { pins.load(reader); } for (String key : new String[] {"poweradvantage_source_ref", "poweradvantage_deobf_sha256", - "basemetals_source_commit", "basemetals_deobf_sha256"}) { + "poweradvantage_player_sha256", "basemetals_source_commit", "basemetals_deobf_sha256", + "basemetals_player_sha256", "orespawn_source_commit", "orespawn_deobf_sha256", + "orespawn_player_sha256"}) { assertTrue(key, pins.getProperty(key, "").matches( key.endsWith("_sha256") ? "[A-F0-9]{64}" : "[a-f0-9]{40}")); } @@ -24,15 +26,16 @@ public class BuildWorkflowContractTest { assertTrue(name, source.contains("BaseMetals-" + pins.getProperty("basemetals_version") + "-deobf.jar")); assertTrue(name, source.contains("repository: " + pins.getProperty("poweradvantage_source_repository"))); assertTrue(name, source.contains("basePower-" + pins.getProperty("poweradvantage_version") + "-deobf.jar")); - assertTrue(name, source.contains("ref: b56e2b62a1a6ea4d1dd96f8646a87801a817dbdf")); + assertTrue(name, source.contains("ref: ${{ env.ORESPAWN_SOURCE_REF }}")); assertTrue(name, source.contains("ref: ${{ env.POWERADVANTAGE_SOURCE_REF }}")); - assertTrue(name, source.contains("The exact basePower 1.17 dependency pin is pending.")); - assertTrue(name, source.contains("The exact Base Metals 1.17 dependency pin is pending.")); + assertTrue(name, source.contains("The exact basePower 1.18 dependency pin is pending.")); + assertTrue(name, source.contains("The exact Base Metals 1.18 dependency pin is pending.")); + assertTrue(name, source.contains("The exact OreSpawn 1.18 dependency pin is pending.")); assertTrue(name, source.contains("^[a-f0-9]{40}$")); assertTrue(name, source.contains("^[A-F0-9]{64}$")); int pinCheck = source.indexOf("Validate coordinated dependency pins"); assertTrue(name, pinCheck >= 0 && pinCheck < source.indexOf("Check out pinned Base Metals") - && pinCheck < source.indexOf("Check out basePower 1.17.1")); + && pinCheck < source.indexOf("Check out basePower 1.18.2")); assertFalse(name, source.contains("-Ppoweradvantage_source_ref=")); assertTrue(name, source.contains("-Porg.gradle.java.installations.paths=")); assertTrue(name, source.contains("-Porg.gradle.java.installations.auto-detect=false")); diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/NativeBlockEntityContractTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/NativeBlockEntityContractTest.java index f9f1f74..ce3d49b 100644 --- a/src/test/java/zone/moddev/mc/basesteam/resources/NativeBlockEntityContractTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/resources/NativeBlockEntityContractTest.java @@ -1,5 +1,6 @@ package zone.moddev.mc.basesteam.resources; +import zone.moddev.mc.basesteam.testing.NativeTestBootstrap; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; @@ -18,7 +19,7 @@ import static org.junit.Assert.*; public class NativeBlockEntityContractTest { - @BeforeClass public static void bootstrap() { net.minecraft.SharedConstants.tryDetectVersion(); net.minecraft.server.Bootstrap.bootStrap(); } + @BeforeClass public static void bootstrap() { NativeTestBootstrap.start(); } @Test public void everyProductionFactoryOwnsItsActualNativePositionAndState() { BlockPos pos = new BlockPos(2053, 81, -2049); diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/NativeBlockPropertiesTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/NativeBlockPropertiesTest.java index f1114bb..45d8860 100644 --- a/src/test/java/zone/moddev/mc/basesteam/resources/NativeBlockPropertiesTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/resources/NativeBlockPropertiesTest.java @@ -1,5 +1,6 @@ package zone.moddev.mc.basesteam.resources; +import zone.moddev.mc.basesteam.testing.NativeTestBootstrap; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -10,7 +11,6 @@ import net.minecraft.world.phys.shapes.BooleanOp; import net.minecraft.world.phys.shapes.VoxelShape; import net.minecraft.world.phys.shapes.Shapes; -import net.minecraft.server.Bootstrap; import net.minecraft.world.level.EmptyBlockGetter; import org.junit.Before; import org.junit.BeforeClass; @@ -35,7 +35,7 @@ public static Collection cases() { @Parameterized.Parameter(1) public int height; private Block block; - @BeforeClass public static void bootstrapNativeRegistries() { net.minecraft.SharedConstants.tryDetectVersion(); Bootstrap.bootStrap(); } + @BeforeClass public static void bootstrapNativeRegistries() { NativeTestBootstrap.start(); } @Before public void createNativeBlockAndStateCaches() { block = height < 0 ? new SteamTrackBlock() : new PlatformBlock(); diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/NativeFuelContractTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/NativeFuelContractTest.java index 29310df..942002b 100644 --- a/src/test/java/zone/moddev/mc/basesteam/resources/NativeFuelContractTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/resources/NativeFuelContractTest.java @@ -1,47 +1,60 @@ package zone.moddev.mc.basesteam.resources; +import zone.moddev.mc.basesteam.testing.NativeTestBootstrap; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.server.packs.PackType; -import net.minecraft.server.packs.resources.SimpleReloadableResourceManager; +import net.minecraft.server.packs.resources.MultiPackResourceManager; import net.minecraft.server.packs.VanillaPackResources; -import net.minecraft.tags.TagCollection; -import net.minecraft.tags.TagContainer; -import net.minecraft.tags.SerializationTags; +import net.minecraft.tags.ItemTags; +import net.minecraft.tags.TagKey; import net.minecraft.tags.TagLoader; -import net.minecraft.tags.StaticTags; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.ResourceKey; +import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraftforge.common.ForgeHooks; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import zone.moddev.mc.basesteam.util.FurnaceFuelHelper; import static org.junit.Assert.*; public class NativeFuelContractTest { + private static Map, List>> previousTags; + @BeforeClass public static void bootstrapNativeFuel() { - net.minecraft.SharedConstants.tryDetectVersion(); net.minecraft.server.Bootstrap.bootStrap(); + NativeTestBootstrap.start(); // Standalone tests need the actual vanilla item tags before the native fuel map. - try (SimpleReloadableResourceManager resources = - new SimpleReloadableResourceManager(PackType.SERVER_DATA)) { - resources.add(new VanillaPackResources(null, "minecraft")); - TagLoader reader = - new TagLoader<>(Registry.ITEM::getOptional, "tags/items"); - TagCollection items = reader.loadAndBuild(resources); - assertTrue(items.getAllTags().containsKey(new ResourceLocation("minecraft:non_flammable_wood"))); - TagContainer current = SerializationTags.getInstance(); - TagContainer loaded = new TagContainer.Builder() - .add(Registry.ITEM_REGISTRY, items).build(); - StaticTags.resetAll(loaded); - SerializationTags.bind(loaded); + try (MultiPackResourceManager resources = new MultiPackResourceManager( + PackType.SERVER_DATA, List.of(new VanillaPackResources(null, "minecraft")))) { + TagLoader> reader = new TagLoader<>( + id -> Registry.ITEM.getHolder(ResourceKey.create(Registry.ITEM_REGISTRY, id)), "tags/items"); + Map, List>> loaded = new HashMap<>(); + reader.loadAndBuild(resources).forEach((id, tag) -> loaded.put(ItemTags.create(id), tag.getValues())); + assertTrue(loaded.containsKey(ItemTags.NON_FLAMMABLE_WOOD)); + previousTags = new HashMap<>(); + Registry.ITEM.getTags().forEach(pair -> previousTags.put(pair.getFirst(), pair.getSecond().stream().toList())); + Registry.ITEM.bindTags(loaded); } ForgeHooks.updateBurns(); } + @AfterClass public static void restoreNativeTags() { + if (previousTags != null) { + Registry.ITEM.bindTags(previousTags); + ForgeHooks.updateBurns(); + } + } + @Test public void keepsExactVanillaAndEmptyFuelValues() { assertEquals(0, FurnaceFuelHelper.getBurnTime(ItemStack.EMPTY)); + assertEquals(0, FurnaceFuelHelper.getBurnTime(new ItemStack(Items.CRIMSON_PLANKS))); + assertEquals(300, FurnaceFuelHelper.getBurnTime(new ItemStack(Items.OAK_PLANKS))); assertEquals(1600, FurnaceFuelHelper.getBurnTime(new ItemStack(Items.COAL))); assertEquals(20000, FurnaceFuelHelper.getBurnTime(new ItemStack(Items.LAVA_BUCKET))); } diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/NativeLootContractTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/NativeLootContractTest.java index 8259db7..1970c55 100644 --- a/src/test/java/zone/moddev/mc/basesteam/resources/NativeLootContractTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/resources/NativeLootContractTest.java @@ -39,8 +39,11 @@ public class NativeLootContractTest { } } - @Test public void packUsesNative117Format() throws Exception { - assertEquals(7, read(ROOT.resolve("pack.mcmeta")).getAsJsonObject("pack").get("pack_format").getAsInt()); + @Test public void packUsesNative118ResourceAndDataFormats() throws Exception { + JsonObject pack = read(ROOT.resolve("pack.mcmeta")).getAsJsonObject("pack"); + assertEquals(9, pack.get("pack_format").getAsInt()); + assertEquals(8, pack.get("forge:resource_pack_format").getAsInt()); + assertEquals(9, pack.get("forge:data_pack_format").getAsInt()); } private static JsonObject read(Path path) throws Exception { diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/NativeRegistrationLifecycleTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/NativeRegistrationLifecycleTest.java new file mode 100644 index 0000000..55ed067 --- /dev/null +++ b/src/test/java/zone/moddev/mc/basesteam/resources/NativeRegistrationLifecycleTest.java @@ -0,0 +1,64 @@ +package zone.moddev.mc.basesteam.resources; + +import java.io.InputStream; +import java.util.Arrays; +import java.util.List; +import org.junit.Test; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.Handle; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.InvokeDynamicInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; +import static org.junit.Assert.*; + +public class NativeRegistrationLifecycleTest { + private static final String ROOT = "zone/moddev/mc/basesteam/"; + private static final String INIT = ROOT + "init/"; + + @Test public void modConstructionDoesNotInitializeRegistriesOrGuis() throws Exception { + assertFalse(calls("SteamAdvantage", "").stream().anyMatch(call -> call.owner.startsWith(INIT))); + } + + @Test public void owningRegisterEventsInitializeBlocksItemsAndEnchantments() throws Exception { + assertTrue(calls("init/Blocks", "registerBlocks").stream().anyMatch(call -> initializes(call, "Blocks"))); + assertTrue(calls("init/Items", "register").stream().anyMatch(call -> initializes(call, "Items"))); + assertTrue(calls("init/Enchantments", "registerEnchantments").stream() + .anyMatch(call -> initializes(call, "Enchantments"))); + assertFalse(calls("init/Blocks", "registerBlockItems").stream().anyMatch(call -> initializes(call, "Blocks"))); + assertFalse(calls("init/Items", "init").stream().anyMatch(call -> initializes(call, "Blocks"))); + assertFalse(calls("init/Recipes", "init").stream() + .anyMatch(call -> initializes(call, "Blocks") || initializes(call, "Items"))); + } + + @Test public void commonSetupQueuesGuiDefinitions() throws Exception { + var instructions = method("SteamAdvantage", "commonSetup").instructions.toArray(); + for (int i = 0; i + 1 < instructions.length; i++) { + if (!(instructions[i] instanceof InvokeDynamicInsnNode runnable)) continue; + if (Arrays.stream(runnable.bsmArgs).anyMatch(arg -> arg instanceof Handle handle + && handle.getOwner().equals(INIT + "GUI") && handle.getName().equals("init"))) { + assertTrue(instructions[i + 1] instanceof MethodInsnNode call && call.name.equals("enqueueWork")); + return; + } + } + fail("Expected GUI initialization on Forge's common setup work queue"); + } + + private static boolean initializes(MethodInsnNode call, String type) { + return call.owner.equals(INIT + type) && call.name.equals("init"); + } + + private static List calls(String type, String name) throws Exception { + return Arrays.stream(method(type, name).instructions.toArray()) + .filter(MethodInsnNode.class::isInstance).map(MethodInsnNode.class::cast).toList(); + } + + private static MethodNode method(String type, String name) throws Exception { + ClassNode node = new ClassNode(); + try (InputStream stream = NativeRegistrationLifecycleTest.class.getResourceAsStream("/" + ROOT + type + ".class")) { + assertNotNull(stream); + new ClassReader(stream).accept(node, 0); + } + return node.methods.stream().filter(method -> method.name.equals(name)).findFirst().orElseThrow(); + } +} diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/OptionalCrushingRecipeContractTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/OptionalCrushingRecipeContractTest.java index e57f3f2..805e7e7 100644 --- a/src/test/java/zone/moddev/mc/basesteam/resources/OptionalCrushingRecipeContractTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/resources/OptionalCrushingRecipeContractTest.java @@ -1,5 +1,6 @@ package zone.moddev.mc.basesteam.resources; +import zone.moddev.mc.basesteam.testing.NativeTestBootstrap; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; @@ -24,8 +25,7 @@ public class OptionalCrushingRecipeContractTest { "steam_furnace", "steam_governor", "steam_tank"}; @BeforeClass public static void bootstrapNativeRegistries() { - net.minecraft.SharedConstants.tryDetectVersion(); - net.minecraft.server.Bootstrap.bootStrap(); + NativeTestBootstrap.start(); } private static JsonObject json(String text) { diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/RecipeUnlockContractTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/RecipeUnlockContractTest.java index 926c421..b8c5ce0 100644 --- a/src/test/java/zone/moddev/mc/basesteam/resources/RecipeUnlockContractTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/resources/RecipeUnlockContractTest.java @@ -1,5 +1,6 @@ package zone.moddev.mc.basesteam.resources; +import zone.moddev.mc.basesteam.testing.NativeTestBootstrap; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -31,7 +32,7 @@ public class RecipeUnlockContractTest { "item:minecraft:stick", "item:minecraft:string", "item:minecraft:feather", "tag:forge:rods/wooden")); @BeforeClass public static void bootstrapNativeRegistries() { - net.minecraft.SharedConstants.tryDetectVersion(); net.minecraft.server.Bootstrap.bootStrap(); + NativeTestBootstrap.start(); } @Test public void everyCraftingRecipeHasExactlyOneUnlockAndProcessingHasNone() throws IOException { diff --git a/src/test/java/zone/moddev/mc/basesteam/testing/NativeTestBootstrap.java b/src/test/java/zone/moddev/mc/basesteam/testing/NativeTestBootstrap.java new file mode 100644 index 0000000..589f515 --- /dev/null +++ b/src/test/java/zone/moddev/mc/basesteam/testing/NativeTestBootstrap.java @@ -0,0 +1,16 @@ +package zone.moddev.mc.basesteam.testing; + +import net.minecraft.SharedConstants; +import net.minecraft.server.Bootstrap; +import net.minecraftforge.registries.GameData; + +public final class NativeTestBootstrap { + private NativeTestBootstrap() {} + + public static void start() { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + // Standalone fixtures construct blocks and items outside Forge's mod registration phase. + GameData.unfreezeData(); + } +} diff --git a/tools/verify-native-port.js b/tools/verify-native-port.js index d36cd4f..f11f964 100644 --- a/tools/verify-native-port.js +++ b/tools/verify-native-port.js @@ -22,10 +22,12 @@ for (const name of files(resources).filter(name => /\.(json|mcmeta)$/.test(name) assert(!/steamadvantage|poweradvantage/.test(name + text), name); json.set(name, JSON.parse(text)); } -assert.deepEqual(json.get(`${resources}/pack.mcmeta`).pack, {pack_format: 7, description: 'baseSteam'}); +assert.deepEqual(json.get(`${resources}/pack.mcmeta`).pack, { + pack_format: 9, 'forge:resource_pack_format': 8, 'forge:data_pack_format': 9, description: 'baseSteam' +}); const metadata = read(`${resources}/META-INF/mods.toml`); assert.match(metadata, /modId="basesteam"/); -assert.match(metadata, /modId="basepower"\r?\nmandatory=true\r?\nversionRange="\[2\.4\.9\.117011,3\.0\.0\)"/); +assert.match(metadata, /modId="basepower"\r?\nmandatory=true\r?\nversionRange="\[2\.4\.10\.118021,3\.0\.0\)"/); assert.match(metadata, /modId="basemetals"\r?\nmandatory=false/); assert.doesNotMatch(metadata, /dependencies\.steamadvantage|modId="(?:steamadvantage|poweradvantage)"/); @@ -85,5 +87,5 @@ for (const id of pickaxe.values.map(value => value.split(':')[1]).filter(id => ! } const tests = files('src/test/java').filter(name => name.endsWith('.java')); const testCount = tests.reduce((sum, name) => sum + (read(name).match(/@Test\b/g) || []).length, 0); -console.log(`BASESTEAM_NATIVE117_SOURCE_PASS json=${json.size} java=${source.length} factories=${ids.length} harvest=${pickaxe.values.length} testMethods=${testCount}`); +console.log(`BASESTEAM_NATIVE118_SOURCE_PASS json=${json.size} java=${source.length} factories=${ids.length} harvest=${pickaxe.values.length} testMethods=${testCount}`); console.log('Source checks only. Compilation, executed tests, packaged runtime and manual acceptance are separate.');