diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2488dd4b..d49dabe0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,16 +1,16 @@ -name: basePower 1.18 CI +name: basePower 1.19 CI on: push: - branches: [master-1.18.2, 'feature/**'] + branches: [master-1.19.4, 'feature/**'] pull_request: - branches: [master-1.18.2] + branches: [master-1.19.4] permissions: contents: read concurrency: - group: basepower-1.18-${{ github.workflow }}-${{ github.ref }} + group: basepower-1.19-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: @@ -25,7 +25,7 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: repository: MinecraftModDevelopmentMods/OreSpawn - ref: d31f52f296739f34a0c13f554abf3e1ac071ae25 + ref: 444d86ef48db2bf54422901b067c51d5832197fc path: .ci-dependencies/OreSpawn - name: Use canonical LF dependency inputs # Use the same source bytes on hosted and local dependency builds. @@ -34,7 +34,7 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: repository: MinecraftModDevelopmentMods/BaseMetals - ref: 3488ed58403722ac3ff203cff7680dfc16e0fd3f + ref: a9fdd6842f2a2e1e2e5b9a0abf9178d5675df044 path: .ci-dependencies/BaseMetals - name: Install pinned Java 25 Mavenizer runtime uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 @@ -67,38 +67,41 @@ jobs: set -euo pipefail orespawn="$GITHUB_WORKSPACE/.ci-dependencies/OreSpawn" basemetals="$GITHUB_WORKSPACE/.ci-dependencies/BaseMetals" + mavenizer="$orespawn/ci-fixtures/tools/minecraft-mavenizer-0.5.21-orespawn-compat.jar" chmod +x "$orespawn/gradlew" "$basemetals/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.118021-deobf.jar" - bmjar="$basemetals/build/libs-dev/BaseMetals-3.1.2.118021-deobf.jar" + osjar="$orespawn/build/libs-dev/OreSpawn-4.1.0.119041-deobf.jar" + bmjar="$basemetals/build/libs-dev/BaseMetals-3.1.2.119041-deobf.jar" test -f "$osjar" && test -f "$bmjar" echo "orespawn=$osjar" >> "$GITHUB_OUTPUT" echo "basemetals=$bmjar" >> "$GITHUB_OUTPUT" + echo "mavenizer=$mavenizer" >> "$GITHUB_OUTPUT" - name: Build and audit release artifacts run: >- ./gradlew $MMD_GRADLE_TOOLCHAINS clean check build javadoc verifyReleaseDependencies verifyReleaseArtifacts writeReleaseChecksums verifyEclipseProductionClasspath -PbaseMetalsDeobfJar=${{ steps.dependencies.outputs.basemetals }} -PoreSpawnDeobfJar=${{ steps.dependencies.outputs.orespawn }} + -Pforge45MavenizerJar=${{ steps.dependencies.outputs.mavenizer }} --no-daemon --stacktrace - name: Upload audited release candidate uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: - name: basePower-1.18.2-${{ github.sha }} + name: basePower-1.19.4-${{ github.sha }} if-no-files-found: error retention-days: 30 path: | - build/libs/basePower-2.4.10.118021.jar - build/libs/basePower-2.4.10.118021-sources.jar - build/libs/basePower-2.4.10.118021-javadoc.jar + build/libs/basePower-2.4.11.119041.jar + build/libs/basePower-2.4.11.119041-sources.jar + build/libs/basePower-2.4.11.119041-javadoc.jar build/release/SHA256SUMS CHANGELOG.txt - name: Upload diagnostics on failure if: failure() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: - name: basePower-1.18.2-diagnostics-${{ github.sha }} + name: basePower-1.19.4-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 a9cca5eb..d0fd9336 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -2,9 +2,9 @@ name: CodeQL on: push: - branches: [master-1.18.2, 'feature/**'] + branches: [master-1.19.4, 'feature/**'] pull_request: - branches: [master-1.18.2] + branches: [master-1.19.4] schedule: - cron: '47 7 * * 4' @@ -25,7 +25,7 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: repository: MinecraftModDevelopmentMods/OreSpawn - ref: d31f52f296739f34a0c13f554abf3e1ac071ae25 + ref: 444d86ef48db2bf54422901b067c51d5832197fc path: .ci-dependencies/OreSpawn - name: Use canonical LF dependency inputs # Use the same source bytes on hosted and local dependency builds. @@ -34,7 +34,7 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: repository: MinecraftModDevelopmentMods/BaseMetals - ref: 3488ed58403722ac3ff203cff7680dfc16e0fd3f + ref: a9fdd6842f2a2e1e2e5b9a0abf9178d5675df044 path: .ci-dependencies/BaseMetals - name: Install pinned Java 25 Mavenizer runtime uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 @@ -67,11 +67,13 @@ jobs: set -euo pipefail orespawn="$GITHUB_WORKSPACE/.ci-dependencies/OreSpawn" basemetals="$GITHUB_WORKSPACE/.ci-dependencies/BaseMetals" + mavenizer="$orespawn/ci-fixtures/tools/minecraft-mavenizer-0.5.21-orespawn-compat.jar" chmod +x "$orespawn/gradlew" "$basemetals/gradlew" (cd "$orespawn" && ./gradlew $MMD_GRADLE_TOOLCHAINS jar --no-daemon --stacktrace) (cd "$basemetals" && ./gradlew $MMD_GRADLE_TOOLCHAINS jar --no-daemon --stacktrace) - echo "orespawn=$orespawn/build/libs-dev/OreSpawn-4.1.0.118021-deobf.jar" >> "$GITHUB_OUTPUT" - echo "basemetals=$basemetals/build/libs-dev/BaseMetals-3.1.2.118021-deobf.jar" >> "$GITHUB_OUTPUT" + echo "orespawn=$orespawn/build/libs-dev/OreSpawn-4.1.0.119041-deobf.jar" >> "$GITHUB_OUTPUT" + echo "basemetals=$basemetals/build/libs-dev/BaseMetals-3.1.2.119041-deobf.jar" >> "$GITHUB_OUTPUT" + echo "mavenizer=$mavenizer" >> "$GITHUB_OUTPUT" - name: Initialize CodeQL uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4 with: @@ -81,6 +83,7 @@ jobs: chmod +x ./gradlew ./gradlew $MMD_GRADLE_TOOLCHAINS clean classes --rerun-tasks --no-build-cache --no-daemon --stacktrace \ -PbaseMetalsDeobfJar=${{ steps.dependencies.outputs.basemetals }} \ - -PoreSpawnDeobfJar=${{ steps.dependencies.outputs.orespawn }} + -PoreSpawnDeobfJar=${{ steps.dependencies.outputs.orespawn }} \ + -Pforge45MavenizerJar=${{ steps.dependencies.outputs.mavenizer }} - name: Analyze uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4 diff --git a/.github/workflows/validate-gradle-build.yml b/.github/workflows/validate-gradle-build.yml index afe21adc..2d52c08f 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.18.2 + - master-1.19.4 - 'feature/**' pull_request: branches: - - master-1.18.2 + - master-1.19.4 permissions: contents: read diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 0f89f797..7a1faeb0 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,12 @@ +basePower 2.4.11.119041 (Minecraft 1.19.4) + +- Bring basePower to Forge 45 and Java 17 without renaming existing machines or items. +- Use the Forge creative tab, fluid types and capabilities. The three mods still share one tab. +- Load the existing machine and oil textures into the new Minecraft texture atlas. +- Adapt the world upgrade hooks to the new save reader and retain the old block, item and machine mappings. +- Keep saved painting variants and recognise the two unused villager points of interest that Minecraft removes when upgrading a 1.18.2 world. +- Keep shared test sources in the right packages so the Eclipse project imports without Java errors. + basePower 2.4.10.118021 (Minecraft 1.18.2) - Bring basePower to Forge 40 and Java 17, keeping the same blocks, items and machine data. diff --git a/README.md b/README.md index ad901af2..68426971 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ # basePower -**Minecraft 1.18.2, version 2.4.10.118021.** +**Minecraft 1.19.4, version 2.4.11.119041.** -See the [1.18 port record](docs/1.18/PORT-STATUS.md) for the current checks. +See the [1.19 port record](docs/1.19/PORT-STATUS.md) for the current checks. Results from earlier versions do not qualify this build. basePower continues DrCyano's Power Advantage. It provides shared power, fluids and item transport for baseSteam and baseElectric. **Back up the entire world before upgrading. Test only disposable copies.** -The port keeps existing registry names and machine data. I tested disposable -copies of original Advantage and released base* saves, including connected -machines, and checked them again after closing and restarting Minecraft. -Check that the other mods in your world support 1.18.2 before you upgrade. +The port keeps existing registry names and machine data. The upgrade checks +use disposable copies of original Advantage and released base* saves, including +connected machines, and check them again after a full restart. Check that the +other mods in your world support 1.19.4 before you upgrade. The accepted [1.14 results](docs/1.14/BROADER-QUALIFICATION.md) are historical evidence, not qualification of this port or a whole modpack. @@ -20,22 +20,24 @@ Recipe-book discovery now follows relevant ingredients. See the [all-Advantage m The [manufacturing progression audit](docs/1.13/MANUFACTURING-PROGRESSION.md) traces survival parts from raw resources, including configured material aliases, processing, loot-only parts and automated restart checks. -## Building Minecraft 1.18.2 +## Building Minecraft 1.19.4 -The build uses Forge 40.3.0, official `1.18.2` mappings, -MCP `20220404.173914`, ForgeGradle 7.0.34 and Gradle 9.6.1. +The build uses Forge 45.4.0, official `1.19.4` mappings, +MCP `20230314.122934`, ForgeGradle 7.0.34 and Gradle 9.6.1. Use Java 17 for Gradle, compilation and Minecraft. Mavenizer also needs Java 25. CI pins these JDKs and Java 8 for the legacy renamer. Pass the installed homes using `-Porg.gradle.java.installations.paths=,,` and disable automatic discovery and downloads for a reproducibility check. -Build the pinned Base Metals `3.1.2.118021` and OreSpawn `4.1.0.118021` deobf jars first. +Build the pinned Base Metals `3.1.2.119041` and OreSpawn `4.1.0.119041` deobf jars first. The source pins and checksums are in `gradle.properties`. -Check out the pinned dependency sources with `core.autocrlf=false` so the -Base Metals development jar matches CI. -Use Base Metals 3.1.2 for upgrades with Base Metals installed. Its shared -save hook lets both mods preserve old items and machines before Minecraft -converts the chunk. Older Base Metals builds do not have that hook. +Forge 45 also needs the Mavenizer compatibility jar in the pinned OreSpawn +checkout's `ci-fixtures/tools/` directory. It fixes decompiled Forge sources +on a clean build and uses Java 25. It is not part of the mod or game runtime. +Set `-Pforge45MavenizerJar=` when that checkout is in another location. +Use Base Metals 3.1.2 if it is installed when upgrading a world. It lets +both mods preserve old items and machines before Minecraft converts the +chunk. An older Base Metals jar is not a substitute for this upgrade path. Then run from the repository root: @@ -46,8 +48,8 @@ gradlew.bat clean check build javadoc verifyReleaseDependencies verifyReleaseArt The default dependency jars are: ```text -D:\BaseMetals\BaseMetals 118\BaseMetals\build\libs-dev\BaseMetals-3.1.2.118021-deobf.jar -D:\MinecraftMineralogy\OS118\OreSpawn\build\libs-dev\OreSpawn-4.1.0.118021-deobf.jar +D:\BaseMetals\BaseMetals 119\BaseMetals\build\libs-dev\BaseMetals-3.1.2.119041-deobf.jar +D:\MinecraftMineralogy\OS119\OreSpawn\build\libs-dev\OreSpawn-4.1.0.119041-deobf.jar ``` Other layouts can pass `-PbaseMetalsDeobfJar=` and `-PoreSpawnDeobfJar=`. @@ -69,17 +71,17 @@ Refresh the project in Eclipse, allow its Java build to finish, and launch the g Eclipse copies production resources from `build/resources/main`, not directly from `src/main/resources`. This prevents its builder from overwriting the expanded `mods.toml` with raw `${version}` placeholders. After updating this setup, use **Gradle > Refresh Gradle Project**, then **Project > Clean** before relaunching. After editing resources or version properties, run `processResources` or refresh the Gradle project before the Eclipse build. See the [metadata regression notes](docs/1.13/ECLIPSE-METADATA.md). -To prepare a combined client, build the baseSteam and baseElectric `2.2.11.118021` deobf jars, +To prepare a combined client, build the baseSteam and baseElectric `2.2.12.119041` deobf jars, run `gradlew.bat prepareCombinedEclipseLaunch`, refresh Eclipse, then launch `runClientAllAdvantage.launch`. Inputs must match -`test-rig/profiles/native118/dev-core.json`; alternative layouts can pass +`test-rig/profiles/native119/dev-core.json`; alternative layouts can pass `-PsteamAdvantageDeobfJar=` and `-PelectricAdvantageDeobfJar=`. The ordinary `runClient.launch` remains Power-only. Both use the `run` directory; preparing a launch does not copy, open or qualify a save. -`check` and Eclipse preparation also run `verifyDevelopmentRuntimeArchives`, -which reads every development runtime jar entry and checks its size and CRC. -This catches damaged cached dependencies before the next test launch. +`check` and the Eclipse audit verify that Java files are in the directories +matching their packages. They also read every development runtime jar entry +and check its size and CRC to catch damaged cached dependencies. Keep Eclipse's access-restriction checks enabled and use Java 17 for its project JRE and Minecraft launches. Check visuals, menus and recipe discovery against the @@ -107,6 +109,6 @@ data-pack tags. The shared pre-flattening migration hook remains necessary; its presence is not proof that an upgrade is safe. The [legacy migration guide](docs/1.13/MIGRATION.md) describes the earlier protocols. -The [1.18 status](docs/1.18/PORT-STATUS.md) tracks this port's qualification. +The [1.19 status](docs/1.19/PORT-STATUS.md) tracks this port's qualification. The historical 1.10 handbook remains on the [1.10.2 branch](https://github.com/MinecraftModDevelopmentMods/PowerAdvantage/blob/master-1.10.2/docs/1.10/README.md). diff --git a/build.gradle b/build.gradle index b421ed82..20233c84 100644 --- a/build.gradle +++ b/build.gradle @@ -15,6 +15,8 @@ plugins { id 'net.minecraftforge.gradle' version '7.0.34' } +apply from: 'gradle/forge45-mavenizer.gradle' + group = project.mod_group version = project.mod_version base.archivesName = 'basePower' @@ -132,7 +134,7 @@ 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.18 launches.' + description = 'Merges classes and processed resources for Forge 1.19.4 launches.' dependsOn tasks.named('classes') from sourceSets.main.output into forgeRunModClassesDirectory @@ -167,10 +169,10 @@ repositories { // The default paths mirror the local MMD checkout layout without tying the // build to a drive letter. CI and other layouts can override either path. def baseMetalsDeobfJar = file(providers.gradleProperty('baseMetalsDeobfJar') - .getOrElse('../../../BaseMetals/BaseMetals 118/BaseMetals/build/libs-dev/' + .getOrElse('../../../BaseMetals/BaseMetals 119/BaseMetals/build/libs-dev/' + "BaseMetals-${project.basemetals_version}-deobf.jar")) def oreSpawnDeobfJar = file(providers.gradleProperty('oreSpawnDeobfJar') - .getOrElse('../../../MinecraftMineralogy/OS118/OreSpawn/build/libs-dev/' + .getOrElse('../../../MinecraftMineralogy/OS119/OreSpawn/build/libs-dev/' + "OreSpawn-${project.orespawn_version}-deobf.jar")) dependencies { @@ -182,6 +184,8 @@ dependencies { testImplementation 'junit:junit:4.13.2' } +sourceSets.test.java.srcDir 'test-rig/unit-support' + def compileUnitBootstrap = tasks.register('compileUnitBootstrap', JavaCompile) { source fileTree('test-rig/unit-bootstrap') classpath = files() @@ -334,17 +338,17 @@ tasks.register('verifyReleaseConfiguration') { description = 'Validates release identity, target metadata, and optional integration policy.' doLast { def pack = new JsonSlurper().parse(file('src/main/resources/pack.mcmeta')).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') + if (pack.pack_format != 12 || pack['forge:resource_pack_format'] != 13 || pack['forge:data_pack_format'] != 12) { + throw new GradleException('Minecraft 1.19.4 requires resource pack format 13 and data pack format 12') } - if (project.mod_version != '2.4.10.118021') { + if (project.mod_version != '2.4.11.119041') { throw new GradleException("Unexpected PowerAdvantage release version: ${project.mod_version}") } - if (project.minecraft_version != '1.18.2' - || project.forge_version != '40.3.0' + if (project.minecraft_version != '1.19.4' + || project.forge_version != '45.4.0' || project.mapping_channel != 'official' - || project.mapping_version != '1.18.2' - || project.mcp_version != '20220404.173914') { + || project.mapping_version != '1.19.4' + || project.mcp_version != '20230314.122934') { throw new GradleException('Unexpected Minecraft, Forge, or mappings target') } if (project.loader_name != 'forge' || project.loader_code != '1' @@ -361,19 +365,19 @@ tasks.register('verifyReleaseConfiguration') { } if (project.cf_requirements != 'base-metals,mmd-orespawn' || project.cf_optionals != 'additional-loot-tables' - || project.cf_versions != '1.18.2') { + || project.cf_versions != '1.19.4') { throw new GradleException('Unexpected CurseForge dependency or game-version metadata') } - if (project.basemetals_version != '3.1.2.118021' - || project.orespawn_version != '4.1.0.118021' - || project.basemetals_source_commit != '3488ed58403722ac3ff203cff7680dfc16e0fd3f' - || project.orespawn_source_commit != 'd31f52f296739f34a0c13f554abf3e1ac071ae25') { + if (project.basemetals_version != '3.1.2.119041' + || project.orespawn_version != '4.1.0.119041' + || project.basemetals_source_commit != 'a9fdd6842f2a2e1e2e5b9a0abf9178d5675df044' + || project.orespawn_source_commit != '444d86ef48db2bf54422901b067c51d5832197fc') { throw new GradleException('Unexpected Base Metals or OreSpawn dependency identity') } String mainSource = file( 'src/main/java/zone/moddev/mc/basepower/PowerAdvantage.java').getText('UTF-8') if (!mainSource.contains('MODID = "basepower"') - || !mainSource.contains('VERSION = "2.4.10.118021"') + || !mainSource.contains('VERSION = "2.4.11.119041"') || !mainSource.contains('@Mod(PowerAdvantage.MODID)')) { throw new GradleException('PowerAdvantage source identity changed') } @@ -944,12 +948,34 @@ tasks.register('isolateEclipseProductionRuns') { } } +def verifyJavaPackageLayout = tasks.register('verifyJavaPackageLayout') { + group = 'verification' + description = 'Checks that Java packages match their paths for Eclipse source lookup.' + doLast { + sourceSets.each { sourceSet -> + sourceSet.java.srcDirs.each { root -> + fileTree(root).matching { include '**/*.java' }.files.sort().each { source -> + def declaration = source.getText('UTF-8') =~ /(?m)^\s*package\s+([\w.$]+)\s*;/ + String declared = declaration.find() ? declaration.group(1) : '' + String expected = root.toPath().relativize(source.parentFile.toPath()) + .toString().replace(File.separator, '.') + if (declared != expected) { + throw new GradleException("${relativePath(source)} declares package '${declared}', " + + "but the ${sourceSet.name} source root expects '${expected}'") + } + } + } + } + } +} + tasks.register('verifyEclipseProductionClasspath') { group = 'verification' description = 'Verifies the generated Eclipse project and production launches.' dependsOn tasks.named('eclipse') dependsOn tasks.named('verifyDevelopmentRuntimeArchives') dependsOn tasks.named('isolateEclipseProductionRuns') + dependsOn verifyJavaPackageLayout doLast { File preferencesFile = file('.settings/org.eclipse.buildship.core.prefs') Properties preferences = new Properties() @@ -1064,6 +1090,7 @@ tasks.register('verifyModelAssets') { zip.entries().each { entry -> vanillaAssets.add(entry.name) } } } + def atlas = new JsonSlurper().parse(new File(resourceRoot, 'assets/minecraft/atlases/blocks.json')) def verifyReference = { String reference, String assetType, String sourcePath -> if (reference.startsWith('#')) return int separator = reference.indexOf(':') @@ -1083,6 +1110,10 @@ tasks.register('verifyModelAssets') { "assets/${namespace}/${assetType}/${path}.json") if (assetType == 'textures') { target = new File(resourceRoot, "assets/${namespace}/textures/${path}.png") + if (!atlas.sources.any { it.type == 'directory' && it.prefix == it.source + '/' + && path.startsWith(it.prefix as String) }) { + throw new GradleException("${sourcePath} references texture ${reference} outside the block atlas") + } } if (!target.isFile()) { throw new GradleException( @@ -1173,6 +1204,7 @@ tasks.register('verifyRecipeTagGuards') { } tasks.named('check') { + dependsOn verifyJavaPackageLayout dependsOn tasks.named('verifyReleaseConfiguration') dependsOn tasks.named('verifyDevelopmentRuntimeArchives') dependsOn tasks.named('verifyModelAssets') @@ -1202,9 +1234,9 @@ tasks.register('clientResourceProbeJar', Jar) { // these deobf jars are runtime inputs, never release contents or compile dependencies. def advantageExtensionJars = [ basesteam: file(providers.gradleProperty('steamAdvantageDeobfJar').getOrElse( - '../SteamAdvantage/SteamAdvantage/build/libs-dev/baseSteam-2.2.11.118021-deobf.jar')), + '../SteamAdvantage/SteamAdvantage/build/libs-dev/baseSteam-2.2.12.119041-deobf.jar')), baseelectric: file(providers.gradleProperty('electricAdvantageDeobfJar').getOrElse( - '../ElectricAdvantage/ElectricAdvantage/build/libs-dev/baseElectric-2.2.11.118021-deobf.jar')) + '../ElectricAdvantage/ElectricAdvantage/build/libs-dev/baseElectric-2.2.12.119041-deobf.jar')) ] tasks.register('prepareCombinedEclipseLaunch') { @@ -1214,7 +1246,7 @@ tasks.register('prepareCombinedEclipseLaunch') { dependsOn tasks.named('deobfJar') doLast { File inputProfile = file(providers.gradleProperty('advantageDevelopmentProfile').getOrElse( - 'test-rig/profiles/native118/dev-core.json')) + 'test-rig/profiles/native119/dev-core.json')) def profile = new JsonSlurper().parse(inputProfile) if (profile.powerDeobfSha256) { File powerJar = tasks.named('jar', Jar).get().archiveFile.get().asFile diff --git a/docs/1.19/PORT-STATUS.md b/docs/1.19/PORT-STATUS.md new file mode 100644 index 00000000..c7467b25 --- /dev/null +++ b/docs/1.19/PORT-STATUS.md @@ -0,0 +1,50 @@ +# Minecraft 1.19.4 Port + +This records the Minecraft 1.19.4 qualification. Back up a world before +upgrading and use a disposable copy for tests. Earlier versions' results do +not qualify this build. + +The port targets Forge 45.4.0, official 1.19.4 mappings and Java 17. It uses +Forge's creative tab and fluid types and keeps existing registry names, tank +formats and recipe modes. When Base Metals is installed, use 3.1.2 for the +shared migration hook. Do not substitute an older build during an upgrade. + +The release audit checks package contents, mappings, dependency hashes, Java +bytecode and Eclipse launch classpaths. The separate graphical probe loads +real models and textures and renders the machine screens. A server startup +cannot prove those visuals. + +The Works harness uses Minecraft's flat preset and retains the 21 station +definitions. The migration runners check live and serialized machine state, +verify that the source is unchanged and refuse changed input hashes. + +See [the 1.19 Works rig](../../test-rig/NATIVE119.md) and +[the client probe](../../test-rig/client-resources/README.md) for commands. +Keep local logs and provisional jars out of release artifacts and commits. + +## What was tested + +The test set is basePower `2.4.11.119041`, baseSteam `2.2.12.119041`, and +baseElectric `2.2.12.119041`. The Works profiles record the exact test inputs. + +- The Works rig ran all 21 stations with and without Base Metals. It also + checked harvest drops, oil and fluid transfer, partial network unloading, + extended drill movement, recipe unlocks, and all three OreSpawn profiles. +- Copies of original 1.10, released base* 1.10 and 1.12, and maintained 1.18 + saves kept their machine state and ran again after a full restart. +- Fresh and upgraded worlds each ran for 30 minutes under measured load. + Both resumed operation after a cold restart. An independent audit replayed + their saved station ledgers. +- The combined client checked 94 items, 905 block states, 39 menu layouts + and recipe-book grouping. It caught and retested missing sprites on the + Steam drill bit and Electric turret items. + +The Nether and End fixtures had one historical difference: Forge 45 also +restores the tame wolf's health without any mods installed. Its UUID, +ownership and passenger relationship were preserved. The raw comparison +and the no-mod control are kept separately. + +In Eclipse use `runClientAllAdvantage.launch` from the PowerAdvantage +project. Check live animations, normal menu interaction and recipe discovery +on a copy of a world. In-world hydroturbine orientation and spin are still +manual checks. diff --git a/gradle.properties b/gradle.properties index 5dc17a52..18bb32aa 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,15 +5,15 @@ org.gradle.caching=true org.gradle.parallel=false net.minecraftforge.gradle.merge-source-sets=false -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,) +minecraft_version=1.19.4 +minecraft_version_range=[1.19.4] +forge_version=45.4.0 +forge_installer_sha256=6643BB8C21F409CC528FF656DAF60C206D304CC77BF529BBBAB4B336004DFC53 +forge_version_range=[45.4.0,46) +loader_version_range=[45,) mapping_channel=official -mapping_version=1.18.2 -mcp_version=20220404.173914 +mapping_version=1.19.4 +mcp_version=20230314.122934 loader_name=forge loader_code=1 @@ -26,21 +26,21 @@ curseforge_project_id=1780140 mod_id=basepower mod_name=basePower mod_license=LGPL-3.0-only -mod_version=2.4.10.118021 +mod_version=2.4.11.119041 mod_group=zone.moddev.mc.basepower mod_authors=DrCyano (aka Cyanobacterium) mod_description=API and shared machines for pressure, electricity, quantum power, fluids, and item transport. cf_requirements=base-metals,mmd-orespawn cf_optionals=additional-loot-tables -cf_versions=1.18.2 +cf_versions=1.19.4 -basemetals_version=3.1.2.118021 -basemetals_source_commit=3488ed58403722ac3ff203cff7680dfc16e0fd3f +basemetals_version=3.1.2.119041 +basemetals_source_commit=a9fdd6842f2a2e1e2e5b9a0abf9178d5675df044 basemetals_curse_project_id=240967 -basemetals_deobf_sha256=E4F9E2CDE355275218C79954D3792D000615790C7CBAE5FCEC59636A8C4CA57D +basemetals_deobf_sha256=0AA993D6BADA54B11548EB5BE150CE0939232291F2D215B7DBAF149857976B2F -orespawn_version=4.1.0.118021 -orespawn_source_commit=d31f52f296739f34a0c13f554abf3e1ac071ae25 +orespawn_version=4.1.0.119041 +orespawn_source_commit=444d86ef48db2bf54422901b067c51d5832197fc orespawn_curse_project_id=245586 -orespawn_deobf_sha256=34B25B2116FD91B0050C80393F8356A5CA256E707BF443E4882BE5CFAD1335B6 +orespawn_deobf_sha256=BE6973F68F1C310DFF8CDF89C12B34C62D68CAEA326C0F1528E486EE3ABC1000 diff --git a/gradle/forge45-mavenizer.gradle b/gradle/forge45-mavenizer.gradle new file mode 100644 index 00000000..298b4944 --- /dev/null +++ b/gradle/forge45-mavenizer.gradle @@ -0,0 +1,27 @@ +import java.security.MessageDigest +import org.gradle.jvm.toolchain.JvmVendorSpec + +// OreSpawn's audited Forge 45 fixture fixes eight decompiled record accessors. +def tool = 'ci-fixtures/tools/minecraft-mavenizer-0.5.21-orespawn-compat.jar' +def defaultJar = project.extensions.extraProperties.has('forge45MavenizerDefault') + ? project.forge45MavenizerDefault + : "../../../MinecraftMineralogy/OS119/OreSpawn/${tool}" +def fixture = file(providers.gradleProperty('forge45MavenizerJar').getOrElse(defaultJar)) +def expected = '341B3FB10ABA6EA7DC612FBE519DC290A1C81867AED5DD320ACD783688AC49DE' +if (!fixture.isFile()) { + throw new GradleException("Forge 45 requires OreSpawn's Mavenizer fixture: ${fixture}. " + + "Use -Pforge45MavenizerJar= for another checkout layout.") +} +def actual = MessageDigest.getInstance('SHA-256').digest(fixture.bytes).encodeHex().toString().toUpperCase(Locale.ROOT) +if (actual != expected) { + throw new GradleException("Forge 45 Mavenizer fixture SHA-256 mismatch: expected ${expected}, got ${actual}") +} + +fgtools.configure('mavenizer') { + classpath.from(fixture) + mainClass.set('net.minecraftforge.mcmaven.cli.Main') + javaLauncher.set(javaToolchains.launcherFor { + languageVersion = JavaLanguageVersion.of(25) + vendor = JvmVendorSpec.ADOPTIUM + }) +} diff --git a/src/main/java/cyano/poweradvantage/api/modsupport/rf/TileEntityRFConverter.java b/src/main/java/cyano/poweradvantage/api/modsupport/rf/TileEntityRFConverter.java index d8ec65ac..5c78bf6a 100644 --- a/src/main/java/cyano/poweradvantage/api/modsupport/rf/TileEntityRFConverter.java +++ b/src/main/java/cyano/poweradvantage/api/modsupport/rf/TileEntityRFConverter.java @@ -10,7 +10,7 @@ import net.minecraft.core.Direction; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.util.LazyOptional; -import net.minecraftforge.energy.CapabilityEnergy; +import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.energy.IEnergyStorage; import zone.moddev.mc.basepower.PowerAdvantage; @@ -35,7 +35,7 @@ public void tickUpdate(boolean server) { Direction side = Direction.values()[index]; BlockEntity neighbor = getLevel().getBlockEntity(getBlockPos().relative(side)); if (neighbor == null) return; - neighbor.getCapability(CapabilityEnergy.ENERGY, side.getOpposite()).ifPresent(storage -> { + neighbor.getCapability(ForgeCapabilities.ENERGY, side.getOpposite()).ifPresent(storage -> { int delta = forgeBuffer - halfBuffer; if (delta < 0 && storage.canExtract()) { int extracted = storage.extractEnergy(-delta, true); @@ -66,7 +66,7 @@ public void tickUpdate(boolean server) { @Override public LazyOptional getCapability(@Nonnull Capability capability, @Nullable Direction side) { if (!areCapabilitiesValid()) return LazyOptional.empty(); - return energyCapability != null && capability == CapabilityEnergy.ENERGY ? energyCapability.cast() : super.getCapability(capability, side); + return energyCapability != null && capability == ForgeCapabilities.ENERGY ? energyCapability.cast() : super.getCapability(capability, side); } @Override public void invalidateCaps() { diff --git a/src/main/java/cyano/poweradvantage/api/simple/SimpleMachineGUI.java b/src/main/java/cyano/poweradvantage/api/simple/SimpleMachineGUI.java index bd550c4f..7254d03b 100644 --- a/src/main/java/cyano/poweradvantage/api/simple/SimpleMachineGUI.java +++ b/src/main/java/cyano/poweradvantage/api/simple/SimpleMachineGUI.java @@ -17,7 +17,6 @@ import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.TextComponent; import zone.moddev.mc.basepower.registry.MachineGUIRegistry; import java.util.HashMap; @@ -253,7 +252,7 @@ public class GUIContainer extends net.minecraft.client.gui.screens.inventory.Abs public net.minecraft.client.Minecraft getMinecraft() { return minecraft; } public GUIContainer(Inventory playerItems, net.minecraft.world.Container entity) { - this(new Container(playerItems, entity), playerItems, new TextComponent("Machine")); + this(new Container(playerItems, entity), playerItems, Component.literal("Machine")); } public GUIContainer(Container container, Inventory playerItems, Component title) { @@ -303,7 +302,7 @@ protected void renderBg(PoseStack matrices, float f, int i, int j) { this.blit(x, y + PLAYER_PANEL_TOP, 0, 132, playerInventoryWidth, playerInventoryHeight); com.mojang.blaze3d.systems.RenderSystem.setShaderTexture(0, guiDisplayImage); this.blit(x, y + MACHINE_TOP, 0, 0, guiWidth, MACHINE_HEIGHT); - drawGUIDecorations(entity, this, x, y + MACHINE_TOP, this.getBlitOffset()); + drawGUIDecorations(entity, this, x, y + MACHINE_TOP, getZLevel()); RenderSystem.disableBlend(); } @@ -318,7 +317,7 @@ public void blit(int x, int y, int u, int v, int width, int height) { * @return screen depth coordinate */ public float getZLevel(){ - return this.getBlitOffset(); + return 0.0F; } /** * Gets the width of the GUI diff --git a/src/main/java/cyano/poweradvantage/api/simple/TileEntitySimpleFluidMachine.java b/src/main/java/cyano/poweradvantage/api/simple/TileEntitySimpleFluidMachine.java index fbefe2eb..7c9ae18a 100644 --- a/src/main/java/cyano/poweradvantage/api/simple/TileEntitySimpleFluidMachine.java +++ b/src/main/java/cyano/poweradvantage/api/simple/TileEntitySimpleFluidMachine.java @@ -19,8 +19,6 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.core.Direction; import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.TextComponent; -import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.world.level.material.Fluid; import cyano.poweradvantage.api.fluid.FluidStack; import cyano.poweradvantage.api.fluid.FluidTank; @@ -662,13 +660,13 @@ public Component getDisplayName() { */ @Override public Component getName() { - return this.hasCustomName() ? new TextComponent(this.customName) - : new TranslatableComponent(unlocalizedName); + return this.hasCustomName() ? Component.literal(this.customName) + : Component.translatable(unlocalizedName); } @Override public Component getCustomName() { - return this.hasCustomName() ? new TextComponent(this.customName) : null; + return this.hasCustomName() ? Component.literal(this.customName) : null; } /** diff --git a/src/main/java/cyano/poweradvantage/api/simple/TileEntitySimplePowerMachine.java b/src/main/java/cyano/poweradvantage/api/simple/TileEntitySimplePowerMachine.java index 56e148ae..8f3d8c41 100644 --- a/src/main/java/cyano/poweradvantage/api/simple/TileEntitySimplePowerMachine.java +++ b/src/main/java/cyano/poweradvantage/api/simple/TileEntitySimplePowerMachine.java @@ -17,8 +17,6 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.core.Direction; import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.TextComponent; -import net.minecraft.network.chat.TranslatableComponent; import java.util.Arrays; @@ -650,13 +648,13 @@ public Component getDisplayName() { */ @Override public Component getName() { - return this.hasCustomName() ? new TextComponent(this.customName) - : new TranslatableComponent(unlocalizedName); + return this.hasCustomName() ? Component.literal(this.customName) + : Component.translatable(unlocalizedName); } @Override public Component getCustomName() { - return this.hasCustomName() ? new TextComponent(this.customName) : null; + return this.hasCustomName() ? Component.literal(this.customName) : null; } /** diff --git a/src/main/java/zone/moddev/mc/basepower/PowerAdvantage.java b/src/main/java/zone/moddev/mc/basepower/PowerAdvantage.java index 329d78fd..23b108b5 100644 --- a/src/main/java/zone/moddev/mc/basepower/PowerAdvantage.java +++ b/src/main/java/zone/moddev/mc/basepower/PowerAdvantage.java @@ -42,7 +42,7 @@ public final class PowerAdvantage { public static final String MODID = "basepower"; public static final String NAME = "basePower"; - public static final String VERSION = "2.4.10.118021"; + public static final String VERSION = "2.4.11.119041"; public static final Logger LOGGER = LogManager.getLogger(MODID); private static PowerAdvantage instance; @@ -84,7 +84,6 @@ public PowerAdvantage() { FMLJavaModLoadingContext.get().getModEventBus().addListener( zone.moddev.mc.basepower.recipe.MaterialTags::registerPack); - ItemGroups.init(); Fluids.init(); migrateLegacyOreSpawnConfiguration(); @@ -100,7 +99,6 @@ public PowerAdvantage() { MachineGUIRegistry.registerScreens(); zone.moddev.mc.basepower.client.ClientOilRenderer.configureRenderLayers(); })); - zone.moddev.mc.basepower.client.ClientOilRenderer.register(); }); } diff --git a/src/main/java/zone/moddev/mc/basepower/client/ClientOilRenderer.java b/src/main/java/zone/moddev/mc/basepower/client/ClientOilRenderer.java index 80e2a095..54e78c89 100644 --- a/src/main/java/zone/moddev/mc/basepower/client/ClientOilRenderer.java +++ b/src/main/java/zone/moddev/mc/basepower/client/ClientOilRenderer.java @@ -1,24 +1,15 @@ package zone.moddev.mc.basepower.client; -import net.minecraft.client.renderer.texture.TextureAtlas; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.ItemBlockRenderTypes; -import net.minecraft.resources.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import net.minecraftforge.client.event.TextureStitchEvent; -import net.minecraftforge.common.MinecraftForge; -import zone.moddev.mc.basepower.PowerAdvantage; -/** Forge FluidAttributes handle rendering; only the custom atlas sprites need stitching. */ +/** Configure the translucent render layer for the registered oils. */ @OnlyIn(Dist.CLIENT) public final class ClientOilRenderer { private ClientOilRenderer() {} - public static void register() { - MinecraftForge.EVENT_BUS.addListener(ClientOilRenderer::stitch); - } - public static void configureRenderLayers() { ItemBlockRenderTypes.setRenderLayer(zone.moddev.mc.basepower.init.Blocks.steel_frame, RenderType.cutout()); @@ -35,10 +26,4 @@ public static void configureRenderLayers() { RenderType.translucent()); } - private static void stitch(TextureStitchEvent.Pre event) { - if (!TextureAtlas.LOCATION_BLOCKS.equals(event.getAtlas().location())) return; - for (String name : new String[] {"crude_oil_still", "crude_oil_flow", "refined_oil_still", "refined_oil_flow"}) { - event.addSprite(new ResourceLocation(PowerAdvantage.MODID, "blocks/" + name)); - } - } } diff --git a/src/main/java/zone/moddev/mc/basepower/compat/BaseMetalsCompat.java b/src/main/java/zone/moddev/mc/basepower/compat/BaseMetalsCompat.java index 4dbc14d8..db9bf2e3 100644 --- a/src/main/java/zone/moddev/mc/basepower/compat/BaseMetalsCompat.java +++ b/src/main/java/zone/moddev/mc/basepower/compat/BaseMetalsCompat.java @@ -26,7 +26,7 @@ public static void addCrusherRecipe(String inputTag, ItemStack output) { } public static void addCrusherRecipe(Block input, ItemStack output) { - PowerAdvantage.LOGGER.debug("Crushing recipe for {} is supplied as data in 1.13", input.getRegistryName()); + PowerAdvantage.LOGGER.debug("Crushing recipe for {} is supplied as data", net.minecraftforge.registries.ForgeRegistries.BLOCKS.getKey(input)); } public static ItemStack getSteelPlate() { diff --git a/src/main/java/zone/moddev/mc/basepower/conduitnetwork/ConduitRegistry.java b/src/main/java/zone/moddev/mc/basepower/conduitnetwork/ConduitRegistry.java index 82b09df1..de835f2c 100644 --- a/src/main/java/zone/moddev/mc/basepower/conduitnetwork/ConduitRegistry.java +++ b/src/main/java/zone/moddev/mc/basepower/conduitnetwork/ConduitRegistry.java @@ -16,9 +16,9 @@ import net.minecraft.core.BlockPos; import net.minecraft.world.level.Level; import net.minecraftforge.common.util.LazyOptional; -import net.minecraftforge.energy.CapabilityEnergy; +import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.energy.IEnergyStorage; -import net.minecraftforge.event.world.ChunkEvent; +import net.minecraftforge.event.level.ChunkEvent; import java.util.*; import java.util.concurrent.locks.Lock; @@ -130,7 +130,7 @@ private List collectRequests(Level w, List net, Condui } } else if (e != null && PowerAdvantage.forgeEnergyConversionTable.containsKey(conduitType)) { for (Direction side : Direction.values()) { - LazyOptional capability = e.getCapability(CapabilityEnergy.ENERGY, side); + LazyOptional capability = e.getCapability(ForgeCapabilities.ENERGY, side); if (!capability.isPresent()) continue; capability.ifPresent(storage -> requests.add(Wrappers.wrapForgeEnergyRequest(storage, energyType, PowerAdvantage.forgeEnergyConversionTable.get(conduitType)))); @@ -272,8 +272,7 @@ public void onChunkUnload(ChunkEvent.Unload event) { } private void invalidateChunk(ChunkEvent event) { - if (!(event.getWorld() instanceof Level)) return; - Level world = (Level) event.getWorld(); + if (!(event.getLevel() instanceof Level world)) return; if (world.isClientSide) return; net.minecraft.resources.ResourceKey dimension = world.dimension(); clearIfWorldChanged(world, dimension); diff --git a/src/main/java/zone/moddev/mc/basepower/gui/FluidTankGUI.java b/src/main/java/zone/moddev/mc/basepower/gui/FluidTankGUI.java index 171ebef7..c42ca9b6 100644 --- a/src/main/java/zone/moddev/mc/basepower/gui/FluidTankGUI.java +++ b/src/main/java/zone/moddev/mc/basepower/gui/FluidTankGUI.java @@ -65,10 +65,14 @@ public static void drawFluidFilledRectangle( ) { if (fs == null || fs.getFluid() == null || width <= 0 || height <= 0) return; // The atlas supplies the current animation frame; water's sprite is grayscale. + var fluidStack = zone.moddev.mc.basepower.util.ForgeFluidAdapter.toForge(fs); + var rendering = net.minecraftforge.client.extensions.common.IClientFluidTypeExtensions.of(fs.getFluid()); + var stillTexture = rendering.getStillTexture(fluidStack); + if (stillTexture == null) return; TextureAtlasSprite sprite = guiContainer.getMinecraft().getTextureAtlas(TextureAtlas.LOCATION_BLOCKS) - .apply(fs.getFluid().getAttributes().getStillTexture()); + .apply(stillTexture); com.mojang.blaze3d.systems.RenderSystem.setShaderTexture(0, TextureAtlas.LOCATION_BLOCKS); - int color = fs.getFluid().getAttributes().getColor(zone.moddev.mc.basepower.util.ForgeFluidAdapter.toForge(fs)); + int color = rendering.getTintColor(fluidStack); if (fs.getFluid().isSame(net.minecraft.world.level.material.Fluids.WATER)) { color = guiContainer.getMinecraft().level != null && guiContainer.getMinecraft().player != null ? BiomeColors.getAverageWaterColor(guiContainer.getMinecraft().level, guiContainer.getMinecraft().player.blockPosition()) : 0x3F76E4; diff --git a/src/main/java/zone/moddev/mc/basepower/gui/FunctionalCreativeTab.java b/src/main/java/zone/moddev/mc/basepower/gui/FunctionalCreativeTab.java deleted file mode 100644 index 5963b046..00000000 --- a/src/main/java/zone/moddev/mc/basepower/gui/FunctionalCreativeTab.java +++ /dev/null @@ -1,79 +0,0 @@ -package zone.moddev.mc.basepower.gui; - -import net.minecraft.world.item.CreativeModeTab; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.core.NonNullList; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; -/** - * Uses java 8 function API to lazily pass an item icon - * @author DrCyano - * - */ -public class FunctionalCreativeTab extends CreativeModeTab { - - private final java.util.function.Supplier itemSupplier; - private final java.util.Comparator itemSortingAlgorithm; - private final boolean searchEnabled; - - /** - * Constructor - * - * @param unlocalizedName Name for translation - * @param itemSupplier Function that provides the item used for the icon - * @param itemSortingAlgorithm Algorithm for sorting the items (Comparable interface implementation) - * @param searchBar If true, there will be a search bar above th item gallery - */ - public FunctionalCreativeTab( - String unlocalizedName, - final java.util.function.Supplier itemSupplier, - final java.util.function.BiFunction itemSortingAlgorithm, - boolean searchBar - ) { - super(unlocalizedName); - this.itemSupplier = itemSupplier; - this.itemSortingAlgorithm = new java.util.Comparator() { - @Override - public int compare(ItemStack o1, ItemStack o2) { - return itemSortingAlgorithm.apply(o1, o2); - } - }; - this.searchEnabled = searchBar; - if (this.searchEnabled) setBackgroundSuffix("item_search.png"); - } - - - /** - * Determines if the search bar should be shown for this tab. - * - * @return True to show the bar - */ - @Override - public boolean hasSearchBar() { - return this.searchEnabled; - } - - - /** - * only shows items which have tabToDisplayOn == this - * - * @param itemList All the items in the tab so you can sort them - */ - @Override - @OnlyIn(Dist.CLIENT) - public void fillItemList(NonNullList itemList) { - super.fillItemList(itemList); - itemList.sort(itemSortingAlgorithm); - } - - - /** - * Gets the item used in the tab icon - */ - @OnlyIn(Dist.CLIENT) - @Override - public ItemStack makeIcon() { - return new ItemStack(itemSupplier.get()); - } -} diff --git a/src/main/java/zone/moddev/mc/basepower/init/Blocks.java b/src/main/java/zone/moddev/mc/basepower/init/Blocks.java index 8a6ea007..2a749a94 100644 --- a/src/main/java/zone/moddev/mc/basepower/init/Blocks.java +++ b/src/main/java/zone/moddev/mc/basepower/init/Blocks.java @@ -14,7 +14,9 @@ import net.minecraft.world.item.Item; import net.minecraft.world.item.BlockItem; import net.minecraft.world.effect.MobEffectInstance; -import net.minecraftforge.event.RegistryEvent; +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.RegisterEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import zone.moddev.mc.basepower.PowerAdvantage; @@ -75,7 +77,6 @@ private Blocks() {} public static synchronized void init() { if (initialized) return; - ItemGroups.init(); Fluids.init(); fluid_drain = (GUIBlock) add(new FluidDrainBlock(), "fluid_drain"); @@ -122,30 +123,29 @@ private static GUIBlock filter(String id, Class event) { + public static void registerBlocks(RegisterEvent event) { + if (!ForgeRegistries.Keys.BLOCKS.equals(event.getRegistryKey())) return; init(); - event.getRegistry().registerAll(ALL_BLOCKS.values().toArray(new Block[0])); + event.register(ForgeRegistries.Keys.BLOCKS, helper -> ALL_BLOCKS.forEach((id, block) -> + helper.register(new ResourceLocation(PowerAdvantage.MODID, id), block))); } @SubscribeEvent - public static void registerBlockItems(RegistryEvent.Register event) { + public static void registerBlockItems(RegisterEvent event) { + if (!ForgeRegistries.Keys.ITEMS.equals(event.getRegistryKey())) return; init(); - for (Map.Entry entry : ALL_BLOCKS.entrySet()) { - Item.Properties properties = new Item.Properties(); - if (!"fluid_pipe_terminal".equals(entry.getKey()) - && !(entry.getValue() instanceof LiquidBlock)) { - properties.tab(ItemGroups.tab_powerAdvantage); + event.register(ForgeRegistries.Keys.ITEMS, helper -> ALL_BLOCKS.forEach((id, block) -> { + BlockItem item = new BlockItem(block, new Item.Properties()); + if (!"fluid_pipe_terminal".equals(id) && !(block instanceof LiquidBlock)) { + ItemGroups.show(item); } - BlockItem item = new BlockItem(entry.getValue(), properties); - item.setRegistryName(PowerAdvantage.MODID, entry.getKey()); - event.getRegistry().register(item); - } + helper.register(new ResourceLocation(PowerAdvantage.MODID, id), item); + })); } public static Map getModBlockRegistry() { diff --git a/src/main/java/zone/moddev/mc/basepower/init/Entities.java b/src/main/java/zone/moddev/mc/basepower/init/Entities.java index 2aa52692..6292e346 100644 --- a/src/main/java/zone/moddev/mc/basepower/init/Entities.java +++ b/src/main/java/zone/moddev/mc/basepower/init/Entities.java @@ -2,7 +2,9 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraftforge.event.RegistryEvent; +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.RegisterEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import zone.moddev.mc.basepower.PowerAdvantage; @@ -54,7 +56,8 @@ public final class Entities { private Entities() {} @SubscribeEvent - public static void register(RegistryEvent.Register> event) { + public static void register(RegisterEvent event) { + if (!ForgeRegistries.Keys.BLOCK_ENTITY_TYPES.equals(event.getRegistryKey())) return; PIPE_TERMINAL = register(event, "tileentity.pipe_terminal", TerminalFluidPipeTileEntity::new, Blocks.fluid_pipe_terminal); FLUID_DRAIN = register(event, "tileentity.fluid_drain", FluidDrainTileEntity::new, Blocks.fluid_drain); FLUID_DISCHARGE = register(event, "tileentity.fluid_discharge", FluidDischargeTileEntity::new, Blocks.fluid_discharge); @@ -79,11 +82,10 @@ public static void register(RegistryEvent.Register> event) { } private static BlockEntityType register( - RegistryEvent.Register> event, String id, + RegisterEvent event, String id, BlockEntityType.BlockEntitySupplier factory, net.minecraft.world.level.block.Block... validBlocks) { BlockEntityType type = BlockEntityType.Builder.of(factory, validBlocks).build(null); - type.setRegistryName(PowerAdvantage.MODID, id); - event.getRegistry().register(type); + event.register(ForgeRegistries.Keys.BLOCK_ENTITY_TYPES, new ResourceLocation(PowerAdvantage.MODID, id), () -> type); return type; } } diff --git a/src/main/java/zone/moddev/mc/basepower/init/Fluids.java b/src/main/java/zone/moddev/mc/basepower/init/Fluids.java index eb6b1d7d..0098eaf3 100644 --- a/src/main/java/zone/moddev/mc/basepower/init/Fluids.java +++ b/src/main/java/zone/moddev/mc/basepower/init/Fluids.java @@ -2,6 +2,7 @@ import java.util.IdentityHashMap; import java.util.Map; +import java.util.function.Consumer; import cyano.poweradvantage.api.ConduitType; import net.minecraft.world.level.block.LiquidBlock; @@ -19,11 +20,13 @@ import net.minecraft.world.level.LevelReader; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.client.extensions.common.IClientFluidTypeExtensions; import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fluids.FluidAttributes; +import net.minecraftforge.fluids.FluidType; import net.minecraftforge.fluids.ForgeFlowingFluid; import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.RegisterEvent; import zone.moddev.mc.basepower.PowerAdvantage; /** Native Forge fluids and their persistent PowerAdvantage conduit identities. */ @@ -38,6 +41,8 @@ public final class Fluids { public static PowerFluid.Flowing flowing_crude_oil; public static PowerFluid.Source refined_oil; public static PowerFluid.Flowing flowing_refined_oil; + private static FluidType crudeOilType; + private static FluidType refinedOilType; private static boolean initialized; @@ -51,29 +56,47 @@ public static synchronized void init() { } @SubscribeEvent - public static void registerFluids(RegistryEvent.Register event) { + public static void registerFluids(RegisterEvent event) { + if (ForgeRegistries.Keys.FLUID_TYPES.equals(event.getRegistryKey())) { + crudeOilType = oilType("crude_oil", 850, 6000); + refinedOilType = oilType("refined_oil", 720, 1000); + event.register(ForgeRegistries.Keys.FLUID_TYPES, id("crude_oil"), () -> crudeOilType); + event.register(ForgeRegistries.Keys.FLUID_TYPES, id("refined_oil"), () -> refinedOilType); + } + if (!ForgeRegistries.Keys.FLUIDS.equals(event.getRegistryKey())) return; flowing_crude_oil = new PowerFluid.Flowing(Kind.CRUDE); crude_oil = new PowerFluid.Source(Kind.CRUDE); flowing_refined_oil = new PowerFluid.Flowing(Kind.REFINED); refined_oil = new PowerFluid.Source(Kind.REFINED); - register("flowing_crude_oil", flowing_crude_oil); - register("crude_oil", crude_oil); - register("flowing_refined_oil", flowing_refined_oil); - register("refined_oil", refined_oil); + event.register(ForgeRegistries.Keys.FLUIDS, id("flowing_crude_oil"), () -> flowing_crude_oil); + event.register(ForgeRegistries.Keys.FLUIDS, id("crude_oil"), () -> crude_oil); + event.register(ForgeRegistries.Keys.FLUIDS, id("flowing_refined_oil"), () -> flowing_refined_oil); + event.register(ForgeRegistries.Keys.FLUIDS, id("refined_oil"), () -> refined_oil); registerNewFluid(crude_oil); registerNewFluid(flowing_crude_oil); registerNewFluid(refined_oil); registerNewFluid(flowing_refined_oil); - event.getRegistry().registerAll(crude_oil, flowing_crude_oil, refined_oil, flowing_refined_oil); } - private static void register(String name, Fluid fluid) { - fluid.setRegistryName(PowerAdvantage.MODID, name); + private static ResourceLocation id(String name) { + return new ResourceLocation(PowerAdvantage.MODID, name); + } + + private static FluidType oilType(String name, int density, int viscosity) { + return new FluidType(FluidType.Properties.create().descriptionId("fluid.basepower." + name) + .density(density).viscosity(viscosity).temperature(300)) { + @Override public void initializeClient(Consumer consumer) { + consumer.accept(new IClientFluidTypeExtensions() { + @Override public ResourceLocation getStillTexture() { return id("blocks/" + name + "_still"); } + @Override public ResourceLocation getFlowingTexture() { return id("blocks/" + name + "_flow"); } + }); + } + }; } public static ConduitType registerNewFluid(Fluid fluid) { - ResourceLocation id = fluid.getRegistryName(); + ResourceLocation id = ForgeRegistries.FLUIDS.getKey(fluid); ConduitType type = new ConduitType(id == null ? "unknown_fluid" : id.toString()); FLUID_TYPES.put(fluid, type); return type; @@ -125,15 +148,10 @@ protected PowerFluid(Kind kind) { public Kind getKind() { return kind; } private static ForgeFlowingFluid.Properties properties(Kind kind) { - String name = kind == Kind.CRUDE ? "crude_oil" : "refined_oil"; return new ForgeFlowingFluid.Properties( + () -> kind == Kind.CRUDE ? crudeOilType : refinedOilType, () -> kind == Kind.CRUDE ? crude_oil : refined_oil, - () -> kind == Kind.CRUDE ? flowing_crude_oil : flowing_refined_oil, - FluidAttributes.builder(new ResourceLocation(PowerAdvantage.MODID, "blocks/" + name + "_still"), - new ResourceLocation(PowerAdvantage.MODID, "blocks/" + name + "_flow")) - .translationKey("fluid.basepower." + name).color(0xFFFFFFFF) - .density(kind == Kind.CRUDE ? 850 : 720) - .viscosity(kind == Kind.CRUDE ? 6000 : 1000).temperature(300)) + () -> kind == Kind.CRUDE ? flowing_crude_oil : flowing_refined_oil) .bucket(() -> kind == Kind.CRUDE ? Items.crude_oil_bucket : Items.refined_oil_bucket) .block(() -> kind == Kind.CRUDE ? Blocks.crude_oil_block : Blocks.refined_oil_block) .slopeFindDistance(4).levelDecreasePerBlock(1) diff --git a/src/main/java/zone/moddev/mc/basepower/init/ItemGroups.java b/src/main/java/zone/moddev/mc/basepower/init/ItemGroups.java index 33a497de..8cb066ea 100644 --- a/src/main/java/zone/moddev/mc/basepower/init/ItemGroups.java +++ b/src/main/java/zone/moddev/mc/basepower/init/ItemGroups.java @@ -1,45 +1,47 @@ package zone.moddev.mc.basepower.init; -import zone.moddev.mc.basepower.PowerAdvantage; -import zone.moddev.mc.basepower.gui.FunctionalCreativeTab; +import java.util.Comparator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.CreativeModeTab; import net.minecraft.world.item.Item; import net.minecraft.world.item.BlockItem; -import net.minecraft.world.item.ItemStack; +import net.minecraftforge.event.CreativeModeTabEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; import net.minecraftforge.registries.ForgeRegistries; +import zone.moddev.mc.basepower.PowerAdvantage; -public class ItemGroups { +@Mod.EventBusSubscriber(modid = PowerAdvantage.MODID, bus = Mod.EventBusSubscriber.Bus.MOD) +public final class ItemGroups { public static CreativeModeTab tab_powerAdvantage; + private static final Set VISIBLE_ITEMS = ConcurrentHashMap.newKeySet(); - private static boolean initDone = false; - - public static synchronized void init() { - if (initDone) return; - -// tab_powerAdvantage = FunctionalCreativeTab.create(PowerAdvantage.MODID) -// .setIcon(Items.sprocket) -// .setItemSortingAlgorithm(ItemGroups::itemSort) -// .setSearchable(true); - - tab_powerAdvantage = new FunctionalCreativeTab(PowerAdvantage.MODID, - ()->Items.sprocket, - ItemGroups::itemSort, - false); + private ItemGroups() {} - initDone = true; + /** Add a registered player-facing item from Power or one of its expansions. */ + public static T show(T item) { + VISIBLE_ITEMS.add(item); + return item; } - @SuppressWarnings("unused") - private static int itemSort(ItemStack A, ItemStack B) { - String catA = catagorize(A); - String catB = catagorize(B); - return catA.compareToIgnoreCase(catB); + @SubscribeEvent + public static void register(CreativeModeTabEvent.Register event) { + tab_powerAdvantage = event.registerCreativeModeTab(new ResourceLocation(PowerAdvantage.MODID, "machines"), + builder -> builder.title(Component.translatable("itemGroup." + PowerAdvantage.MODID)) + .icon(() -> Items.sprocket.getDefaultInstance()) + .displayItems((parameters, output) -> VISIBLE_ITEMS.stream() + .sorted(Comparator.comparing(ItemGroups::category, String.CASE_INSENSITIVE_ORDER) + .thenComparing(ForgeRegistries.ITEMS::getKey)) + .forEach(output::accept))); } - private static String catagorize(ItemStack i) { + private static String category(Item item) { StringBuilder sb = new StringBuilder(); - Item item = i.getItem(); sb.append(ForgeRegistries.ITEMS.getKey(item).getNamespace()); if (item instanceof BlockItem) { if (((BlockItem) item).getBlock().defaultBlockState().hasBlockEntity()) { diff --git a/src/main/java/zone/moddev/mc/basepower/init/Items.java b/src/main/java/zone/moddev/mc/basepower/init/Items.java index 29a242f1..92cceb25 100644 --- a/src/main/java/zone/moddev/mc/basepower/init/Items.java +++ b/src/main/java/zone/moddev/mc/basepower/init/Items.java @@ -6,7 +6,9 @@ import net.minecraft.world.item.Item; import net.minecraft.world.item.BucketItem; -import net.minecraftforge.event.RegistryEvent; +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.RegisterEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import zone.moddev.mc.basepower.PowerAdvantage; @@ -29,32 +31,32 @@ private Items() {} public static synchronized void init() { if (initialized) return; - ItemGroups.init(); starch = add("starch", new Item(properties())); bioplastic_ingot = add("bioplastic_ingot", new Item(properties())); sprocket = add("sprocket", new Item(properties())); rotator_tool = add("rotator_tool", new RotationTool(properties().stacksTo(1))); crude_oil_bucket = add("crude_oil_bucket", new BucketItem(() -> Fluids.crude_oil, - new Item.Properties().tab(ItemGroups.tab_powerAdvantage).stacksTo(1).craftRemainder(net.minecraft.world.item.Items.BUCKET))); + properties().stacksTo(1).craftRemainder(net.minecraft.world.item.Items.BUCKET))); refined_oil_bucket = add("refined_oil_bucket", new BucketItem(() -> Fluids.refined_oil, - new Item.Properties().tab(ItemGroups.tab_powerAdvantage).stacksTo(1).craftRemainder(net.minecraft.world.item.Items.BUCKET))); + properties().stacksTo(1).craftRemainder(net.minecraft.world.item.Items.BUCKET))); initialized = true; } private static Item.Properties properties() { - return new Item.Properties().tab(ItemGroups.tab_powerAdvantage); + return new Item.Properties(); } private static Item add(String id, Item item) { - item.setRegistryName(PowerAdvantage.MODID, id); allItems.put(id, item); return item; } @SubscribeEvent - public static void register(RegistryEvent.Register event) { + public static void register(RegisterEvent event) { + if (!ForgeRegistries.Keys.ITEMS.equals(event.getRegistryKey())) return; init(); - event.getRegistry().registerAll(allItems.values().toArray(new Item[0])); + event.register(ForgeRegistries.Keys.ITEMS, helper -> allItems.forEach((id, item) -> + helper.register(new ResourceLocation(PowerAdvantage.MODID, id), ItemGroups.show(item)))); } public static Item getItemByName(String id) { return allItems.get(id); } diff --git a/src/main/java/zone/moddev/mc/basepower/machines/conveyors/TileEntityConveyor.java b/src/main/java/zone/moddev/mc/basepower/machines/conveyors/TileEntityConveyor.java index f74ae2aa..651a0574 100644 --- a/src/main/java/zone/moddev/mc/basepower/machines/conveyors/TileEntityConveyor.java +++ b/src/main/java/zone/moddev/mc/basepower/machines/conveyors/TileEntityConveyor.java @@ -15,9 +15,6 @@ import net.minecraft.core.Direction; import net.minecraft.world.phys.AABB; import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.BaseComponent; -import net.minecraft.network.chat.TextComponent; -import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.network.chat.Component; import net.minecraft.world.MenuProvider; import net.minecraft.world.level.Level; @@ -409,8 +406,8 @@ public void setItem(int slot, ItemStack item) { } @Override - public BaseComponent getDisplayName() { - return (BaseComponent) getName(); + public Component getDisplayName() { + return getName(); } private String customName = null; @@ -421,13 +418,13 @@ public BaseComponent getDisplayName() { */ @Override public Component getName() { - return this.hasCustomName() ? new TextComponent(this.customName) - : new TranslatableComponent(this.getDescriptionId()); + return this.hasCustomName() ? Component.literal(this.customName) + : Component.translatable(this.getDescriptionId()); } @Override public Component getCustomName() { - return this.hasCustomName() ? new TextComponent(this.customName) : null; + return this.hasCustomName() ? Component.literal(this.customName) : null; } /** diff --git a/src/main/java/zone/moddev/mc/basepower/machines/conveyors/TileEntitySmeltableFilter.java b/src/main/java/zone/moddev/mc/basepower/machines/conveyors/TileEntitySmeltableFilter.java index f542ceda..188187d7 100644 --- a/src/main/java/zone/moddev/mc/basepower/machines/conveyors/TileEntitySmeltableFilter.java +++ b/src/main/java/zone/moddev/mc/basepower/machines/conveyors/TileEntitySmeltableFilter.java @@ -2,7 +2,7 @@ import net.minecraft.world.SimpleContainer; import net.minecraft.world.item.ItemStack; -import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.Component; import net.minecraft.world.item.crafting.RecipeType; import zone.moddev.mc.basepower.init.Entities; diff --git a/src/main/java/zone/moddev/mc/basepower/machines/fluidmachines/StorageTankBlock.java b/src/main/java/zone/moddev/mc/basepower/machines/fluidmachines/StorageTankBlock.java index 45637785..ea1473a0 100644 --- a/src/main/java/zone/moddev/mc/basepower/machines/fluidmachines/StorageTankBlock.java +++ b/src/main/java/zone/moddev/mc/basepower/machines/fluidmachines/StorageTankBlock.java @@ -20,8 +20,6 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Registry; import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.TextComponent; -import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import zone.moddev.mc.basepower.init.Fluids; @@ -71,11 +69,11 @@ public void appendHoverText(ItemStack stack, @Nullable BlockGetter world, List tooltip, TooltipFlag flag) { CompoundTag tag = stack.getTag(); if (tag == null || !tag.contains("FluidID", 8) || !tag.contains("Volume", 99)) { - tooltip.add(new TranslatableComponent("tooltip.basepower.empty")); + tooltip.add(Component.translatable("tooltip.basepower.empty")); return; } float buckets = tag.getInt("Volume") / (float) FluidTank.BUCKET_VOLUME; - tooltip.add(new TextComponent(buckets + " buckets of " + tag.getString("FluidID"))); + tooltip.add(Component.literal(buckets + " buckets of " + tag.getString("FluidID"))); } /** Retained for callers from the 1.12 implementation. */ diff --git a/src/main/java/zone/moddev/mc/basepower/math/BlockPos4D.java b/src/main/java/zone/moddev/mc/basepower/math/BlockPos4D.java index 6cfce6b9..03709a29 100644 --- a/src/main/java/zone/moddev/mc/basepower/math/BlockPos4D.java +++ b/src/main/java/zone/moddev/mc/basepower/math/BlockPos4D.java @@ -292,7 +292,7 @@ public static BlockPos4D fromNBT(Tag tag) { CompoundTag data = (CompoundTag) tag; int[] pos = data.getIntArray("Pos"); if (pos.length != 3) throw new IllegalArgumentException("Invalid position"); - return new BlockPos4D(ResourceKey.create(Registry.DIMENSION_REGISTRY, + return new BlockPos4D(ResourceKey.create(net.minecraft.core.registries.Registries.DIMENSION, new ResourceLocation(data.getString("Dimension"))), pos[0], pos[1], pos[2]); } diff --git a/src/main/java/zone/moddev/mc/basepower/migration/LegacyEntityDataFixes.java b/src/main/java/zone/moddev/mc/basepower/migration/LegacyEntityDataFixes.java index 8b55f6aa..6725f4c7 100644 --- a/src/main/java/zone/moddev/mc/basepower/migration/LegacyEntityDataFixes.java +++ b/src/main/java/zone/moddev/mc/basepower/migration/LegacyEntityDataFixes.java @@ -29,7 +29,7 @@ private LegacyEntityDataFixes() {} static void prepare(CompoundTag root) { if (root.contains(STAGED)) return; int version = root.contains("DataVersion", 99) ? root.getInt("DataVersion") : -1; - int current = SharedConstants.getCurrentVersion().getWorldVersion(); + int current = SharedConstants.getCurrentVersion().getDataVersion().getVersion(); if (version >= current) return; CompoundTag level = entityOwner(root); if (level == null) return; diff --git a/src/main/java/zone/moddev/mc/basepower/migration/LegacyMachineItemDataFixes.java b/src/main/java/zone/moddev/mc/basepower/migration/LegacyMachineItemDataFixes.java index 9da5c62d..d255afc5 100644 --- a/src/main/java/zone/moddev/mc/basepower/migration/LegacyMachineItemDataFixes.java +++ b/src/main/java/zone/moddev/mc/basepower/migration/LegacyMachineItemDataFixes.java @@ -22,7 +22,7 @@ private LegacyMachineItemDataFixes() {} static void prepare(CompoundTag root) { int version = root.contains("DataVersion", 99) ? root.getInt("DataVersion") : -1; - int current = SharedConstants.getCurrentVersion().getWorldVersion(); + int current = SharedConstants.getCurrentVersion().getDataVersion().getVersion(); if (version >= current || root.getBoolean(PREPARED) || !root.contains("Level", 10)) return; ListTag tiles = root.getCompound("Level").getList("TileEntities", 10); for (int index = 0; index < tiles.size(); index++) { diff --git a/src/main/java/zone/moddev/mc/basepower/migration/LegacyPlayerDataFixes.java b/src/main/java/zone/moddev/mc/basepower/migration/LegacyPlayerDataFixes.java index c3cf1ce3..bee4ec80 100644 --- a/src/main/java/zone/moddev/mc/basepower/migration/LegacyPlayerDataFixes.java +++ b/src/main/java/zone/moddev/mc/basepower/migration/LegacyPlayerDataFixes.java @@ -4,7 +4,6 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.NbtUtils; import net.minecraft.SharedConstants; import net.minecraft.util.datafix.DataFixTypes; @@ -12,18 +11,22 @@ public final class LegacyPlayerDataFixes { private LegacyPlayerDataFixes() {} + public static CompoundTag updateNativePlayer(DataFixTypes type, DataFixer fixer, CompoundTag source, int version) { + return update(fixer, type, source, version); + } + public static CompoundTag update(DataFixer fixer, DataFixTypes type, CompoundTag source, int version) { if (type != DataFixTypes.PLAYER) throw new IllegalArgumentException("Expected native PLAYER type"); CompoundTag result = source.copy(); - int current = SharedConstants.getCurrentVersion().getWorldVersion(); + int current = SharedConstants.getCurrentVersion().getDataVersion().getVersion(); if (version >= current) return result; LegacyEntityDataFixes.preparePlayerAttributes(result); if (version < 704) { // Re-read the native namespaced container schema before later choice fixes. - result = NbtUtils.update(fixer, type, result, version, 704); + result = type.update(fixer, result, version, 704); version = 704; } - result = NbtUtils.update(fixer, type, result, version, current); + result = type.update(fixer, result, version, current); preserveNames(source, result); return result; } diff --git a/src/main/java/zone/moddev/mc/basepower/migration/LegacyWorldDataHook.java b/src/main/java/zone/moddev/mc/basepower/migration/LegacyWorldDataHook.java index 2d729f38..e1185f00 100644 --- a/src/main/java/zone/moddev/mc/basepower/migration/LegacyWorldDataHook.java +++ b/src/main/java/zone/moddev/mc/basepower/migration/LegacyWorldDataHook.java @@ -204,6 +204,11 @@ public static CompoundTag copyChunkForMigration(CompoundTag root) { return root == null ? null : root.copy(); } + public static java.util.concurrent.CompletableFuture> copyChunkReadForMigration( + java.util.concurrent.CompletableFuture> read) { + return read.thenApply(chunk -> chunk.map(LegacyWorldDataHook::copyChunkForMigration)); + } + /** Native 1.17 stores entities separately; never mutate an I/O worker's cached data. */ public static CompoundTag prepareEntityChunkForMigration(CompoundTag source) { if (source == null || !source.contains("Position", 11) diff --git a/src/main/java/zone/moddev/mc/basepower/migration/LegacyWorldMetadata.java b/src/main/java/zone/moddev/mc/basepower/migration/LegacyWorldMetadata.java index 7d174957..f0214a4f 100644 --- a/src/main/java/zone/moddev/mc/basepower/migration/LegacyWorldMetadata.java +++ b/src/main/java/zone/moddev/mc/basepower/migration/LegacyWorldMetadata.java @@ -32,7 +32,15 @@ public static Tag remapLightweight(Tag root) { } static void remap(CompoundTag root, Predicate loaded) { - ListTag mods = root.getCompound("fml").getList("LoadingModList", Tag.TAG_COMPOUND); + CompoundTag forge = root.getCompound("fml"); + CompoundTag registries = forge.getCompound("Registries"); + if (registries.contains("minecraft:motive", Tag.TAG_COMPOUND) + && !registries.contains("minecraft:painting_variant")) { + // The registry was renamed; keep its saved IDs, including modded entries. + registries.put("minecraft:painting_variant", registries.getCompound("minecraft:motive")); + registries.remove("minecraft:motive"); + } + ListTag mods = forge.getList("LoadingModList", Tag.TAG_COMPOUND); Set recordedMods = new HashSet<>(); for (int i = 0; i < mods.size(); ++i) recordedMods.add(mods.getCompound(i).getString("ModId")); for (int i = 0; i < mods.size(); ++i) { diff --git a/src/main/java/zone/moddev/mc/basepower/migration/VanillaRegistryRemaps.java b/src/main/java/zone/moddev/mc/basepower/migration/VanillaRegistryRemaps.java index 099256e7..be4fd5fe 100644 --- a/src/main/java/zone/moddev/mc/basepower/migration/VanillaRegistryRemaps.java +++ b/src/main/java/zone/moddev/mc/basepower/migration/VanillaRegistryRemaps.java @@ -16,8 +16,8 @@ import net.minecraft.util.datafix.fixes.CavesAndCliffsRenames; import net.minecraft.world.level.biome.Biome; import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.event.RegistryEvent; -import net.minecraftforge.registries.IForgeRegistryEntry; +import net.minecraftforge.eventbus.api.EventPriority; +import net.minecraftforge.registries.MissingMappingsEvent; import net.minecraftforge.registries.IForgeRegistry; import net.minecraftforge.registries.ForgeRegistries; import zone.moddev.mc.basepower.PowerAdvantage; @@ -29,86 +29,93 @@ public final class VanillaRegistryRemaps { private VanillaRegistryRemaps() {} public static void register() { - MinecraftForge.EVENT_BUS.addGenericListener(Block.class, VanillaRegistryRemaps::blocks); - MinecraftForge.EVENT_BUS.addGenericListener(Item.class, VanillaRegistryRemaps::items); - MinecraftForge.EVENT_BUS.addGenericListener(EntityType.class, VanillaRegistryRemaps::entities); - MinecraftForge.EVENT_BUS.addGenericListener(SoundEvent.class, VanillaRegistryRemaps::sounds); - MinecraftForge.EVENT_BUS.addGenericListener(Biome.class, VanillaRegistryRemaps::biomes); - MinecraftForge.EVENT_BUS.addGenericListener(MemoryModuleType.class, VanillaRegistryRemaps::memories); - MinecraftForge.EVENT_BUS.addGenericListener(BlockEntityType.class, VanillaRegistryRemaps::blockEntities); - MinecraftForge.EVENT_BUS.addGenericListener(Fluid.class, VanillaRegistryRemaps::fluids); - MinecraftForge.EVENT_BUS.addGenericListener(Enchantment.class, VanillaRegistryRemaps::enchantments); - MinecraftForge.EVENT_BUS.addGenericListener(MenuType.class, VanillaRegistryRemaps::menus); - MinecraftForge.EVENT_BUS.addGenericListener(RecipeSerializer.class, VanillaRegistryRemaps::recipeSerializers); + MinecraftForge.EVENT_BUS.addListener(EventPriority.HIGHEST, VanillaRegistryRemaps::blocks); + MinecraftForge.EVENT_BUS.addListener(EventPriority.HIGHEST, VanillaRegistryRemaps::items); + MinecraftForge.EVENT_BUS.addListener(EventPriority.HIGHEST, VanillaRegistryRemaps::entities); + MinecraftForge.EVENT_BUS.addListener(EventPriority.HIGHEST, VanillaRegistryRemaps::sounds); + MinecraftForge.EVENT_BUS.addListener(EventPriority.HIGHEST, VanillaRegistryRemaps::biomes); + MinecraftForge.EVENT_BUS.addListener(EventPriority.HIGHEST, VanillaRegistryRemaps::memories); + MinecraftForge.EVENT_BUS.addListener(EventPriority.HIGHEST, VanillaRegistryRemaps::pointsOfInterest); + MinecraftForge.EVENT_BUS.addListener(EventPriority.HIGHEST, VanillaRegistryRemaps::blockEntities); + MinecraftForge.EVENT_BUS.addListener(EventPriority.HIGHEST, VanillaRegistryRemaps::fluids); + MinecraftForge.EVENT_BUS.addListener(EventPriority.HIGHEST, VanillaRegistryRemaps::enchantments); + MinecraftForge.EVENT_BUS.addListener(EventPriority.HIGHEST, VanillaRegistryRemaps::menus); + MinecraftForge.EVENT_BUS.addListener(EventPriority.HIGHEST, VanillaRegistryRemaps::recipeSerializers); } - private static void blocks(RegistryEvent.MissingMappings event) { + private static void blocks(MissingMappingsEvent event) { remap(event, ForgeRegistries.BLOCKS); remapAdvantage(event, ForgeRegistries.BLOCKS, LegacyMigrationApi::blockTarget); } - private static void items(RegistryEvent.MissingMappings event) { + private static void items(MissingMappingsEvent event) { remap(event, ForgeRegistries.ITEMS); remapAdvantage(event, ForgeRegistries.ITEMS, LegacyMigrationApi::itemTarget); } - private static void entities(RegistryEvent.MissingMappings> event) { - remap(event, ForgeRegistries.ENTITIES); - remapAdvantage(event, ForgeRegistries.ENTITIES, + private static void entities(MissingMappingsEvent event) { + remap(event, ForgeRegistries.ENTITY_TYPES); + remapAdvantage(event, ForgeRegistries.ENTITY_TYPES, id -> new ResourceLocation(LegacyMigrationApi.entityTarget(id.toString()))); } - private static void sounds(RegistryEvent.MissingMappings event) { remap(event, ForgeRegistries.SOUND_EVENTS); } - private static void biomes(RegistryEvent.MissingMappings event) { remap(event, ForgeRegistries.BIOMES); } - private static void memories(RegistryEvent.MissingMappings> event) { remap(event, ForgeRegistries.MEMORY_MODULE_TYPES); } + private static void sounds(MissingMappingsEvent event) { remap(event, ForgeRegistries.SOUND_EVENTS); } + private static void biomes(MissingMappingsEvent event) { remap(event, ForgeRegistries.BIOMES); } + private static void memories(MissingMappingsEvent event) { remap(event, ForgeRegistries.MEMORY_MODULE_TYPES); } + private static void pointsOfInterest(MissingMappingsEvent event) { remap(event, ForgeRegistries.POI_TYPES); } - private static void blockEntities(RegistryEvent.MissingMappings> event) { - remapAdvantage(event, ForgeRegistries.BLOCK_ENTITIES, + private static void blockEntities(MissingMappingsEvent event) { + remapAdvantage(event, ForgeRegistries.BLOCK_ENTITY_TYPES, id -> new ResourceLocation(LegacyMigrationApi.tileTarget(id.toString()))); } - private static void fluids(RegistryEvent.MissingMappings event) { + private static void fluids(MissingMappingsEvent event) { remapAdvantage(event, ForgeRegistries.FLUIDS, id -> new ResourceLocation(LegacyMigrationApi.fluidTarget(id.toString()))); } - private static void enchantments(RegistryEvent.MissingMappings event) { + private static void enchantments(MissingMappingsEvent event) { remapAdvantage(event, ForgeRegistries.ENCHANTMENTS, LegacyMigrationApi::registryTarget); } - private static void menus(RegistryEvent.MissingMappings> event) { - remapAdvantage(event, ForgeRegistries.CONTAINERS, LegacyMigrationApi::registryTarget); + private static void menus(MissingMappingsEvent event) { + remapAdvantage(event, ForgeRegistries.MENU_TYPES, LegacyMigrationApi::registryTarget); } - private static void recipeSerializers(RegistryEvent.MissingMappings> event) { + private static void recipeSerializers(MissingMappingsEvent event) { remapAdvantage(event, ForgeRegistries.RECIPE_SERIALIZERS, LegacyMigrationApi::registryTarget); } - private static > void remapAdvantage(RegistryEvent.MissingMappings event, + private static void remapAdvantage(MissingMappingsEvent event, IForgeRegistry active, UnaryOperator replacement) { - for (RegistryEvent.MissingMappings.Mapping mapping : event.getAllMappings()) { - if (mapping.getAction() != RegistryEvent.MissingMappings.Action.DEFAULT) continue; - ResourceLocation target = replacement.apply(mapping.key); - if (!mapping.key.equals(target) && active.containsKey(target)) { + for (MissingMappingsEvent.Mapping mapping : event.getAllMappings(active.getRegistryKey())) { + ResourceLocation target = replacement.apply(mapping.getKey()); + if (!mapping.getKey().equals(target) && active.containsKey(target)) { mapping.remap(active.getValue(target)); - PowerAdvantage.LOGGER.info("Migrated Advantage registry entry {} to {}", mapping.key, target); + PowerAdvantage.LOGGER.info("Migrated Advantage registry entry {} to {}", mapping.getKey(), target); } } } - static > void remap(RegistryEvent.MissingMappings event, IForgeRegistry active) { - for (RegistryEvent.MissingMappings.Mapping mapping : event.getMappings("minecraft")) { - if (mapping.getAction() != RegistryEvent.MissingMappings.Action.DEFAULT) continue; - if (retiredTransientMemory(event.getName(), mapping.key)) { + static void remap(MissingMappingsEvent event, IForgeRegistry active) { + for (MissingMappingsEvent.Mapping mapping : event.getMappings(active.getRegistryKey(), "minecraft")) { + if (retiredTransientMemory(active.getRegistryName(), mapping.getKey())) { // Both old types used register(name) without a serializer: no saved // brain values exist. Retire only their obsolete Forge ID indexes. mapping.ignore(); - PowerAdvantage.LOGGER.info("Retired non-serialized vanilla AI registry index {}", mapping.key); + PowerAdvantage.LOGGER.info("Retired non-serialized vanilla AI registry index {}", mapping.getKey()); continue; } - ResourceLocation updatedBiome = vanillaBiomeFixTarget(event.getName(), mapping.key); + if (retiredVanillaPoi(active.getRegistryName(), mapping.getKey())) { + // Vanilla's "Remove unpopulated villager PoI types" fixer handles + // saved POI sections. Forge still sees the obsolete numeric indexes. + mapping.ignore(); + PowerAdvantage.LOGGER.info("Retired unused vanilla point of interest index {}", mapping.getKey()); + continue; + } + ResourceLocation updatedBiome = vanillaBiomeFixTarget(active.getRegistryName(), mapping.getKey()); if (updatedBiome != null && active.containsKey(updatedBiome)) { // Vanilla fixes biome names in chunks and world settings. Several // old entries share one target; do not assign it multiple numeric IDs. mapping.ignore(); PowerAdvantage.LOGGER.info("Retired vanilla biome registry index {} (data fixer: {})", - mapping.key, updatedBiome); + mapping.getKey(), updatedBiome); continue; } - ResourceLocation replacement = replacement(event.getName(), mapping.key); + ResourceLocation replacement = replacement(active.getRegistryName(), mapping.getKey()); if (replacement == null) continue; // The event's registry is STAGING: new entries need not be present there yet. if (!active.containsKey(replacement)) { @@ -116,7 +123,7 @@ static > void remap(RegistryEvent.MissingMappin } T target = active.getValue(replacement); mapping.remap(target); - PowerAdvantage.LOGGER.info("Migrated vanilla registry entry {} to {}", mapping.key, replacement); + PowerAdvantage.LOGGER.info("Migrated vanilla registry entry {} to {}", mapping.getKey(), replacement); } } @@ -126,6 +133,12 @@ static boolean retiredTransientMemory(ResourceLocation registry, ResourceLocatio && (old.getPath().equals("golem_last_seen_time") || old.getPath().equals("opened_doors")); } + static boolean retiredVanillaPoi(ResourceLocation registry, ResourceLocation old) { + return registry.equals(new ResourceLocation("minecraft:point_of_interest_type")) + && old.getNamespace().equals("minecraft") + && (old.getPath().equals("nitwit") || old.getPath().equals("unemployed")); + } + static ResourceLocation vanillaBiomeFixTarget(ResourceLocation registry, ResourceLocation old) { if (!BIOME_REGISTRY.equals(registry) || !"minecraft".equals(old.getNamespace())) return null; String name = old.toString(); diff --git a/src/main/java/zone/moddev/mc/basepower/recipe/MaterialPolicyPack.java b/src/main/java/zone/moddev/mc/basepower/recipe/MaterialPolicyPack.java index 1f61425e..79b46eb5 100644 --- a/src/main/java/zone/moddev/mc/basepower/recipe/MaterialPolicyPack.java +++ b/src/main/java/zone/moddev/mc/basepower/recipe/MaterialPolicyPack.java @@ -3,28 +3,27 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; import java.io.ByteArrayInputStream; -import java.io.FileNotFoundException; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; -import java.util.function.Predicate; -import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.PackResources; import net.minecraft.server.packs.PackType; import net.minecraft.server.packs.metadata.MetadataSectionSerializer; import net.minecraft.server.packs.metadata.pack.PackMetadataSection; +import net.minecraft.server.packs.resources.IoSupplier; import net.minecraftforge.fml.ModList; import zone.moddev.mc.basepower.PowerAdvantage; import zone.moddev.mc.basepower.RecipeMode; /** Additive native tags: server policy is resolved and synchronized by vanilla. */ final class MaterialPolicyPack implements PackResources { - private static final String NAME = "Power Advantage material aliases"; + static final String ID = "basepower:material_policy"; + static final String NAME = "Power Advantage material aliases"; private final Map resources = new LinkedHashMap<>(); MaterialPolicyPack() { @@ -57,38 +56,29 @@ private void put(String path, JsonArray values) { resources.put(new ResourceLocation("forge", "tags/items/" + path + ".json"), tag.toString()); } - @Override public InputStream getRootResource(String name) throws FileNotFoundException { - throw new FileNotFoundException(name); - } + @Override public IoSupplier getRootResource(String... names) { return null; } - @Override public InputStream getResource(PackType type, ResourceLocation id) throws FileNotFoundException { - if (!hasResource(type, id)) throw new FileNotFoundException(id.toString()); - return new ByteArrayInputStream(resources.get(id).getBytes(StandardCharsets.UTF_8)); + @Override public IoSupplier getResource(PackType type, ResourceLocation id) { + if (type != PackType.SERVER_DATA || !resources.containsKey(id)) return null; + return () -> new ByteArrayInputStream(resources.get(id).getBytes(StandardCharsets.UTF_8)); } - @Override public Collection getResources(PackType type, String namespace, - String prefix, int depth, Predicate filter) { - if (type != PackType.SERVER_DATA) return Collections.emptyList(); - java.util.List found = new java.util.ArrayList<>(); + @Override public void listResources(PackType type, String namespace, String prefix, ResourceOutput output) { + if (type != PackType.SERVER_DATA) return; for (ResourceLocation id : resources.keySet()) { - String path = id.getPath(); - if (namespace.equals(id.getNamespace()) && path.startsWith(prefix + "/") - && filter.test(path.substring(path.lastIndexOf('/') + 1))) found.add(id); + if (namespace.equals(id.getNamespace()) && id.getPath().startsWith(prefix + "/")) { + output.accept(id, getResource(type, id)); + } } - return found; - } - - @Override public boolean hasResource(PackType type, ResourceLocation id) { - return type == PackType.SERVER_DATA && resources.containsKey(id); } @Override public Set getNamespaces(PackType type) { return type == PackType.SERVER_DATA ? Collections.singleton("forge") : Collections.emptySet(); } @Override @SuppressWarnings("unchecked") public T getMetadataSection(MetadataSectionSerializer serializer) { - return serializer == PackMetadataSection.SERIALIZER - ? (T)new PackMetadataSection(new TextComponent(NAME), 9) : null; + return serializer == PackMetadataSection.TYPE + ? (T)new PackMetadataSection(Component.literal(NAME), 12) : null; } - @Override public String getName() { return NAME; } + @Override public String packId() { return ID; } @Override public void close() {} } diff --git a/src/main/java/zone/moddev/mc/basepower/recipe/MaterialTags.java b/src/main/java/zone/moddev/mc/basepower/recipe/MaterialTags.java index 2ee5736e..ce20d88f 100644 --- a/src/main/java/zone/moddev/mc/basepower/recipe/MaterialTags.java +++ b/src/main/java/zone/moddev/mc/basepower/recipe/MaterialTags.java @@ -6,9 +6,10 @@ private MaterialTags() {} public static void registerPack(net.minecraftforge.event.AddPackFindersEvent event) { if (event.getPackType() != net.minecraft.server.packs.PackType.SERVER_DATA) return; - event.addRepositorySource((consumer, constructor) -> { - net.minecraft.server.packs.repository.Pack pack = net.minecraft.server.packs.repository.Pack.create( - "basepower:material_policy", true, MaterialPolicyPack::new, constructor, + event.addRepositorySource(consumer -> { + net.minecraft.server.packs.repository.Pack pack = net.minecraft.server.packs.repository.Pack.readMetaAndCreate( + MaterialPolicyPack.ID, net.minecraft.network.chat.Component.literal(MaterialPolicyPack.NAME), + true, id -> new MaterialPolicyPack(), event.getPackType(), net.minecraft.server.packs.repository.Pack.Position.BOTTOM, net.minecraft.server.packs.repository.PackSource.BUILT_IN); if (pack != null) consumer.accept(pack); diff --git a/src/main/java/zone/moddev/mc/basepower/recipe/RecipeConditions.java b/src/main/java/zone/moddev/mc/basepower/recipe/RecipeConditions.java index 3f2e654e..3d16854c 100644 --- a/src/main/java/zone/moddev/mc/basepower/recipe/RecipeConditions.java +++ b/src/main/java/zone/moddev/mc/basepower/recipe/RecipeConditions.java @@ -36,14 +36,13 @@ private Condition(ResourceLocation id, String value) { @Override public ResourceLocation getID() { return id; } - @Override public boolean test() { return test(IContext.EMPTY); } @Override public boolean test(IContext context) { switch (id.getPath()) { case "recipe_mode": return PowerAdvantage.recipeMode == RecipeMode.valueOf(value.toUpperCase(Locale.ROOT)); case "not_recipe_mode": return PowerAdvantage.recipeMode != RecipeMode.valueOf(value.toUpperCase(Locale.ROOT)); - case "tag_empty": return context.getTag(ItemTags.create(new ResourceLocation(value))).getValues().isEmpty(); - case "tag_not_empty": return !context.getTag(ItemTags.create(new ResourceLocation(value))).getValues().isEmpty(); + case "tag_empty": return context.getTag(ItemTags.create(new ResourceLocation(value))).isEmpty(); + case "tag_not_empty": return !context.getTag(ItemTags.create(new ResourceLocation(value))).isEmpty(); default: throw new IllegalStateException("Unknown Advantage condition " + id); } } diff --git a/src/main/java/zone/moddev/mc/basepower/recipe/RecipeDiscovery.java b/src/main/java/zone/moddev/mc/basepower/recipe/RecipeDiscovery.java index 08d0401f..8fdd8b5d 100644 --- a/src/main/java/zone/moddev/mc/basepower/recipe/RecipeDiscovery.java +++ b/src/main/java/zone/moddev/mc/basepower/recipe/RecipeDiscovery.java @@ -18,8 +18,7 @@ public final class RecipeDiscovery { private RecipeDiscovery() {} public static void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent event) { - if (!(event.getPlayer() instanceof ServerPlayer)) return; - ServerPlayer player = (ServerPlayer) event.getPlayer(); + if (!(event.getEntity() instanceof ServerPlayer player)) return; // Disabled recipes have no reward to grant. Replay only already-earned // discoveries when that exact recipe becomes available on a later login. for (Recipe recipe : player.getServer().getRecipeManager().getRecipes()) { diff --git a/src/main/java/zone/moddev/mc/basepower/registry/FuelRegistry.java b/src/main/java/zone/moddev/mc/basepower/registry/FuelRegistry.java index 6b73b291..fdef43ca 100644 --- a/src/main/java/zone/moddev/mc/basepower/registry/FuelRegistry.java +++ b/src/main/java/zone/moddev/mc/basepower/registry/FuelRegistry.java @@ -159,7 +159,7 @@ public static ItemStack decrementFuelItem(ItemStack fuelItem){ if(list.isEmpty()){ fuelItem.shrink(1); if(fuelItem.isEmpty()){ - fuelItem = fuelItem.getItem().getContainerItem(fuelItem); + fuelItem = fuelItem.getCraftingRemainingItem(); } } else { for (Function algorithm : list) { diff --git a/src/main/java/zone/moddev/mc/basepower/registry/MachineGUIRegistry.java b/src/main/java/zone/moddev/mc/basepower/registry/MachineGUIRegistry.java index 139745dd..b693e161 100644 --- a/src/main/java/zone/moddev/mc/basepower/registry/MachineGUIRegistry.java +++ b/src/main/java/zone/moddev/mc/basepower/registry/MachineGUIRegistry.java @@ -15,12 +15,13 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.world.level.Level; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.common.extensions.IForgeMenuType; -import net.minecraftforge.event.RegistryEvent; +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.RegisterEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.network.NetworkHooks; @@ -51,10 +52,10 @@ public static int addGUI(ITileEntityGUI gui) { public void removeGUI(int id) { DEFINITIONS.remove(id); } @SubscribeEvent - public static void registerTypes(RegistryEvent.Register> event) { + public static void registerTypes(RegisterEvent event) { + if (!ForgeRegistries.Keys.MENU_TYPES.equals(event.getRegistryKey())) return; TYPE = IForgeMenuType.create(MachineGUIRegistry::createClientContainer); - TYPE.setRegistryName(PowerAdvantage.MODID, "machine"); - event.getRegistry().register(TYPE); + event.register(ForgeRegistries.Keys.MENU_TYPES, new ResourceLocation(PowerAdvantage.MODID, "machine"), () -> TYPE); } private static SimpleMachineGUI.Container createClientContainer( @@ -88,7 +89,7 @@ public static void open(ServerPlayer player, Level world, BlockPos pos, int id) BlockEntity tile = world.getBlockEntity(pos); if (world.isClientSide || player.level != world || !(tile instanceof Container)) return; SimpleMachineGUI gui = definition(id); - NetworkHooks.openGui(player, new MenuProvider() { + NetworkHooks.openScreen(player, new MenuProvider() { @Override public AbstractContainerMenu createMenu(int windowId, Inventory inventory, Player owner) { return gui.createMenu(windowId, inventory, (Container) tile); } @@ -103,7 +104,7 @@ static Component menuTitle(BlockEntity tile, Block block) { Component customName = ((net.minecraft.world.Nameable) tile).getCustomName(); if (customName != null) return customName; } - return new TranslatableComponent(block.getDescriptionId()); + return Component.translatable(block.getDescriptionId()); } public Object getServerGuiElement(int id, Player player, Level world, int x, int y, int z) { diff --git a/src/main/java/zone/moddev/mc/basepower/util/AdvantageFluidAdapter.java b/src/main/java/zone/moddev/mc/basepower/util/AdvantageFluidAdapter.java index e7faf21d..9b63e8da 100644 --- a/src/main/java/zone/moddev/mc/basepower/util/AdvantageFluidAdapter.java +++ b/src/main/java/zone/moddev/mc/basepower/util/AdvantageFluidAdapter.java @@ -6,7 +6,7 @@ import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.core.Direction; -import net.minecraftforge.fluids.capability.CapabilityFluidHandler; +import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction; @@ -21,7 +21,7 @@ final class AdvantageFluidAdapter implements IAdvantageFluidHandler { } private IFluidHandler handler() { - return tile.isRemoved() ? null : tile.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, side).orElse(null); + return tile.isRemoved() ? null : tile.getCapability(ForgeCapabilities.FLUID_HANDLER, side).orElse(null); } @Override public FluidTankInfo[] getTankInfo(Direction ignored) { diff --git a/src/main/java/zone/moddev/mc/basepower/util/FluidHandlerHelper.java b/src/main/java/zone/moddev/mc/basepower/util/FluidHandlerHelper.java index e904588d..ce8da75d 100644 --- a/src/main/java/zone/moddev/mc/basepower/util/FluidHandlerHelper.java +++ b/src/main/java/zone/moddev/mc/basepower/util/FluidHandlerHelper.java @@ -11,7 +11,7 @@ private FluidHandlerHelper() {} public static IAdvantageFluidHandler getHandler(BlockEntity tileEntity, Direction face) { if (tileEntity == null || tileEntity.isRemoved()) return null; if (tileEntity instanceof IAdvantageFluidHandler) return (IAdvantageFluidHandler) tileEntity; - return tileEntity.getCapability(net.minecraftforge.fluids.capability.CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, face).isPresent() + return tileEntity.getCapability(net.minecraftforge.common.capabilities.ForgeCapabilities.FLUID_HANDLER, face).isPresent() ? new AdvantageFluidAdapter(tileEntity, face) : null; } diff --git a/src/main/java/zone/moddev/mc/basepower/util/InventoryWrapper.java b/src/main/java/zone/moddev/mc/basepower/util/InventoryWrapper.java index d0aef060..8502239b 100644 --- a/src/main/java/zone/moddev/mc/basepower/util/InventoryWrapper.java +++ b/src/main/java/zone/moddev/mc/basepower/util/InventoryWrapper.java @@ -8,7 +8,6 @@ import net.minecraft.core.Direction; import net.minecraft.world.Nameable; import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.TextComponent; /** Gives ordinary inventories the sided interface used by conveyors. */ public class InventoryWrapper implements WorldlyContainer, Nameable { @@ -32,7 +31,7 @@ public static WorldlyContainer wrap(Container inventory) { public boolean isLocked() { return InventoryLocks.isLocked(inventory); } @Override public Component getName() { - return inventory instanceof Nameable ? ((Nameable) inventory).getName() : new TextComponent("Inventory"); + return inventory instanceof Nameable ? ((Nameable) inventory).getName() : Component.literal("Inventory"); } @Override public Component getCustomName() { return inventory instanceof Nameable ? ((Nameable) inventory).getCustomName() : null; diff --git a/src/main/java/zone/moddev/mc/basepower/util/MachineCapabilities.java b/src/main/java/zone/moddev/mc/basepower/util/MachineCapabilities.java index 6551a015..31b5e81e 100644 --- a/src/main/java/zone/moddev/mc/basepower/util/MachineCapabilities.java +++ b/src/main/java/zone/moddev/mc/basepower/util/MachineCapabilities.java @@ -9,8 +9,7 @@ import net.minecraft.core.Direction; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.util.LazyOptional; -import net.minecraftforge.fluids.capability.CapabilityFluidHandler; -import net.minecraftforge.items.CapabilityItemHandler; +import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.items.wrapper.InvWrapper; import net.minecraftforge.items.wrapper.SidedInvWrapper; @@ -25,11 +24,11 @@ public final class MachineCapabilities { public LazyOptional get(Capability capability, Direction face) { if (!valid || owner.isRemoved()) return LazyOptional.empty(); - if (capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY && owner instanceof IAdvantageFluidHandler) { + if (capability == ForgeCapabilities.FLUID_HANDLER && owner instanceof IAdvantageFluidHandler) { return fluids.computeIfAbsent(face, side -> LazyOptional.of(() -> new ForgeFluidAdapter((IAdvantageFluidHandler) owner, side, owner::setChanged))).cast(); } - if (capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY && owner instanceof Container) { + if (capability == ForgeCapabilities.ITEM_HANDLER && owner instanceof Container) { return items.computeIfAbsent(face, side -> LazyOptional.of(() -> side != null && owner instanceof WorldlyContainer ? new SidedInvWrapper((WorldlyContainer) owner, side) : new InvWrapper((Container) owner))).cast(); } diff --git a/src/main/java/zone/moddev/mc/basepower/util/PowerHelper.java b/src/main/java/zone/moddev/mc/basepower/util/PowerHelper.java index 5b6b7db8..2260fe1f 100644 --- a/src/main/java/zone/moddev/mc/basepower/util/PowerHelper.java +++ b/src/main/java/zone/moddev/mc/basepower/util/PowerHelper.java @@ -6,7 +6,7 @@ import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.core.Direction; -import net.minecraftforge.energy.CapabilityEnergy; +import net.minecraftforge.common.capabilities.ForgeCapabilities; /** * Collection of utility methods @@ -32,7 +32,7 @@ public static boolean areConnectable(PowerConnectorContext connection) { BlockEntity tile = connection.world.getBlockEntity(connection.otherBlockPosition); if (tile == null) return false; for (Direction side : Direction.values()) { - if (tile.getCapability(CapabilityEnergy.ENERGY, side).isPresent()) return true; + if (tile.getCapability(ForgeCapabilities.ENERGY, side).isPresent()) return true; } } } diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 7e15753b..410e4c28 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -14,14 +14,14 @@ description='''API and shared machines for pressure, electricity, quantum power, [[dependencies.basepower]] modId="basemetals" mandatory=false -versionRange="[3.1.2.118021,4.0.0)" +versionRange="[3.1.2.119041,4.0.0)" ordering="AFTER" side="BOTH" [[dependencies.basepower]] modId="orespawn" mandatory=true -versionRange="[4.1.0.118021,5.0.0)" +versionRange="[4.1.0.119041,5.0.0)" ordering="AFTER" side="BOTH" diff --git a/src/main/resources/assets/minecraft/atlases/blocks.json b/src/main/resources/assets/minecraft/atlases/blocks.json new file mode 100644 index 00000000..2f13b278 --- /dev/null +++ b/src/main/resources/assets/minecraft/atlases/blocks.json @@ -0,0 +1,7 @@ +{ + "sources": [ + { "type": "directory", "source": "blocks", "prefix": "blocks/" }, + { "type": "directory", "source": "items", "prefix": "items/" }, + { "type": "directory", "source": "materials", "prefix": "materials/" } + ] +} diff --git a/src/main/resources/coremods/poweradvantage_118_migration.js b/src/main/resources/coremods/poweradvantage_118_migration.js index c0ed94b0..ab66bc13 100644 --- a/src/main/resources/coremods/poweradvantage_118_migration.js +++ b/src/main/resources/coremods/poweradvantage_118_migration.js @@ -13,7 +13,6 @@ var metadata = 'zone/moddev/mc/basepower/migration/LegacyWorldMetadata'; function patchPlayerDataFix(classNode) { var descriptor = '(Lcom/mojang/datafixers/DataFixer;' + - 'Lnet/minecraft/util/datafix/DataFixTypes;' + 'Lnet/minecraft/nbt/CompoundTag;I)Lnet/minecraft/nbt/CompoundTag;'; var matches = 0; var matchedMethod = null; @@ -22,8 +21,8 @@ function patchPlayerDataFix(classNode) { var method = classNode.methods.get(i); for (var instruction = method.instructions.getFirst(); instruction !== null; instruction = instruction.getNext()) { - if (instruction.getOpcode() === Opcodes.INVOKESTATIC && - instruction.owner === 'net/minecraft/nbt/NbtUtils' && instruction.desc === descriptor) { + if (instruction.getOpcode() === Opcodes.INVOKEVIRTUAL && + instruction.owner === 'net/minecraft/util/datafix/DataFixTypes' && instruction.desc === descriptor) { ++matches; matchedMethod = method; matchedCall = instruction; @@ -34,7 +33,8 @@ function patchPlayerDataFix(classNode) { throw new Error('Power Advantage requires exactly one native player datafix call in ' + classNode.name); } matchedMethod.instructions.set(matchedCall, new MethodInsnNode(Opcodes.INVOKESTATIC, - 'zone/moddev/mc/basepower/migration/LegacyPlayerDataFixes', 'update', descriptor, false)); + 'zone/moddev/mc/basepower/migration/LegacyPlayerDataFixes', 'updateNativePlayer', + '(Lnet/minecraft/util/datafix/DataFixTypes;' + descriptor.substring(1), false)); return classNode; } @@ -56,16 +56,18 @@ function initializeCoreMod() { for (var i = 0; i < classNode.methods.size(); ++i) { var method = classNode.methods.get(i); var worldReader = method.desc === '(Lcom/mojang/serialization/DynamicOps;' + - 'Lnet/minecraft/world/level/DataPackConfig;' + + 'Lnet/minecraft/world/level/WorldDataConfiguration;Lnet/minecraft/core/Registry;' + 'Lcom/mojang/serialization/Lifecycle;' + - 'Ljava/io/File;Lcom/mojang/datafixers/DataFixer;)' + - 'Lnet/minecraft/world/level/storage/PrimaryLevelData;'; - var packReader = method.desc === '(Ljava/io/File;Lcom/mojang/datafixers/DataFixer;)' + - 'Lnet/minecraft/world/level/DataPackConfig;'; + 'Ljava/nio/file/Path;Lcom/mojang/datafixers/DataFixer;)' + + 'Lcom/mojang/datafixers/util/Pair;'; + var packReader = method.desc === '(Ljava/nio/file/Path;Lcom/mojang/datafixers/DataFixer;)' + + 'Lnet/minecraft/world/level/WorldDataConfiguration;'; if (!worldReader && !packReader) continue; if (worldReader) { var prefix = new InsnList(); - prefix.add(new VarInsnNode(Opcodes.ALOAD, 3)); + prefix.add(new VarInsnNode(Opcodes.ALOAD, 4)); + prefix.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, + 'java/nio/file/Path', 'toFile', '()Ljava/io/File;', true)); prefix.add(new MethodInsnNode(Opcodes.INVOKESTATIC, hook, 'prepareLegacyWorld', '(Ljava/io/File;)V', false)); method.instructions.insert(prefix); @@ -75,7 +77,7 @@ function initializeCoreMod() { var reads = 0; for (var read = method.instructions.getFirst(); read !== null; read = read.getNext()) { if (read.getOpcode() === Opcodes.INVOKESTATIC && read.owner === classNode.name && - read.desc === '(Ljava/io/File;)Lnet/minecraft/nbt/Tag;') { + read.desc === '(Ljava/nio/file/Path;)Lnet/minecraft/nbt/Tag;') { method.instructions.insert(read, new MethodInsnNode(Opcodes.INVOKESTATIC, metadata, 'remapLightweight', '(Lnet/minecraft/nbt/Tag;)Lnet/minecraft/nbt/Tag;', false)); ++reads; @@ -103,7 +105,8 @@ function initializeCoreMod() { call.desc === '(Ljava/io/File;)Lnet/minecraft/nbt/CompoundTag;') read = call; if (call.owner === 'net/minecraftforge/common/ForgeHooks' && call.name === 'readAdditionalLevelSaveData' && - call.desc === '(Lnet/minecraft/nbt/CompoundTag;Ljava/nio/file/Path;)V') { + call.desc === '(Lnet/minecraft/nbt/CompoundTag;' + + 'Lnet/minecraft/world/level/storage/LevelStorageSource$LevelDirectory;)V') { if (read === null) throw new Error('Power Advantage expected Forge to read level.dat'); method.instructions.set(read, new MethodInsnNode(Opcodes.INVOKESTATIC, metadata, 'read', read.desc, false)); @@ -127,12 +130,12 @@ function initializeCoreMod() { var patchedEntities = false; for (var i = 0; i < classNode.methods.size(); ++i) { var method = classNode.methods.get(i); - if (method.desc === '(Lnet/minecraft/world/level/ChunkPos;)Lnet/minecraft/nbt/CompoundTag;') { + if (method.desc === '(Lnet/minecraft/world/level/ChunkPos;)Ljava/util/concurrent/CompletableFuture;') { for (var read = method.instructions.getFirst(); read !== null; read = read.getNext()) { if (read.getOpcode() === Opcodes.ARETURN) { method.instructions.insertBefore(read, new MethodInsnNode(Opcodes.INVOKESTATIC, - hook, 'copyChunkForMigration', - '(Lnet/minecraft/nbt/CompoundTag;)Lnet/minecraft/nbt/CompoundTag;', false)); + hook, 'copyChunkReadForMigration', + '(Ljava/util/concurrent/CompletableFuture;)Ljava/util/concurrent/CompletableFuture;', false)); patchedOwnership = true; } } diff --git a/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta index 91c42b11..fbf929c9 100644 --- a/src/main/resources/pack.mcmeta +++ b/src/main/resources/pack.mcmeta @@ -1,8 +1,8 @@ { "pack": { "description": "Power Advantage resources", - "pack_format": 9, - "forge:resource_pack_format": 8, - "forge:data_pack_format": 9 + "pack_format": 12, + "forge:resource_pack_format": 13, + "forge:data_pack_format": 12 } } diff --git a/src/test/java/zone/moddev/mc/basepower/machines/fluidmachines/WorldFluidPickupTest.java b/src/test/java/zone/moddev/mc/basepower/machines/fluidmachines/WorldFluidPickupTest.java index 48415db2..802af20e 100644 --- a/src/test/java/zone/moddev/mc/basepower/machines/fluidmachines/WorldFluidPickupTest.java +++ b/src/test/java/zone/moddev/mc/basepower/machines/fluidmachines/WorldFluidPickupTest.java @@ -14,7 +14,13 @@ import static org.junit.Assert.*; public class WorldFluidPickupTest { - @BeforeClass public static void bootstrap() { net.minecraft.SharedConstants.tryDetectVersion(); net.minecraft.server.Bootstrap.bootStrap(); } + @BeforeClass public static void bootstrap() { + net.minecraft.SharedConstants.tryDetectVersion(); + net.minecraft.server.Bootstrap.bootStrap(); + // Forge normally rebuilds these caches after the mod registry phase. + Blocks.WATER.getStateDefinition().getPossibleStates().forEach(BlockState::initCache); + Blocks.OAK_SLAB.getStateDefinition().getPossibleStates().forEach(BlockState::initCache); + } @Test public void waterloggedHostSurvivesTheNativePickup() { AtomicReference state = new AtomicReference<>(Blocks.OAK_SLAB.defaultBlockState() diff --git a/src/test/java/zone/moddev/mc/basepower/math/BlockPos4DTest.java b/src/test/java/zone/moddev/mc/basepower/math/BlockPos4DTest.java index 0ea4e69b..d13cdea6 100644 --- a/src/test/java/zone/moddev/mc/basepower/math/BlockPos4DTest.java +++ b/src/test/java/zone/moddev/mc/basepower/math/BlockPos4DTest.java @@ -4,7 +4,7 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.core.BlockPos; -import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; import net.minecraft.world.level.Level; import org.junit.BeforeClass; import org.junit.Test; @@ -14,8 +14,8 @@ public class BlockPos4DTest { @BeforeClass public static void bootstrap() { net.minecraft.SharedConstants.tryDetectVersion(); net.minecraft.server.Bootstrap.bootStrap(); } @Test public void customDimensionKeysNeverShareCoordinates() { - ResourceKey first = ResourceKey.create(Registry.DIMENSION_REGISTRY, new ResourceLocation("test:first")); - ResourceKey second = ResourceKey.create(Registry.DIMENSION_REGISTRY, new ResourceLocation("test:second")); + ResourceKey first = ResourceKey.create(Registries.DIMENSION, new ResourceLocation("test:first")); + ResourceKey second = ResourceKey.create(Registries.DIMENSION, new ResourceLocation("test:second")); BlockPos4D pos = new BlockPos4D(first, new BlockPos(-17, 80, 32)); assertNotEquals(pos, new BlockPos4D(second, pos.pos)); assertEquals(pos, BlockPos4D.fromNBT(pos.toNBT())); diff --git a/src/test/java/zone/moddev/mc/basepower/migration/LegacyEntitiesTest.java b/src/test/java/zone/moddev/mc/basepower/migration/LegacyEntitiesTest.java index 01b07117..137af57e 100644 --- a/src/test/java/zone/moddev/mc/basepower/migration/LegacyEntitiesTest.java +++ b/src/test/java/zone/moddev/mc/basepower/migration/LegacyEntitiesTest.java @@ -63,7 +63,7 @@ public class LegacyEntitiesTest { CompoundTag wolf = entity("minecraft:wolf"); wolf.putString("OwnerUUID", "a614e658-6270-33c3-9297-7ce80908964f"); wolf.put("CustomData", entity("Wolf")); - CompoundTag root = chunk(wolf, SharedConstants.getCurrentVersion().getWorldVersion()); + CompoundTag root = chunk(wolf, SharedConstants.getCurrentVersion().getDataVersion().getVersion()); LegacyWorldDataHook.prepareLegacyChunk(root); LegacyWorldDataHook.prepareLegacyChunk(root); assertEquals(wolf, root.getCompound("Level").getList("Entities", 10).getCompound(0)); @@ -164,16 +164,16 @@ private static void assertWolf(boolean customEntity) { assertEquals(owner, actual.getUUID("Owner")); assertEquals(8F, actual.getFloat("Health"), 0F); assertTrue(actual.getBoolean("Sitting")); - converted.putInt("DataVersion", SharedConstants.getCurrentVersion().getWorldVersion()); - assertEquals(converted, convert(converted.copy(), SharedConstants.getCurrentVersion().getWorldVersion())); + converted.putInt("DataVersion", SharedConstants.getCurrentVersion().getDataVersion().getVersion()); + assertEquals(converted, convert(converted.copy(), SharedConstants.getCurrentVersion().getDataVersion().getVersion())); } } private static CompoundTag convert(CompoundTag root, int version) { LegacyWorldDataHook.prepareLegacyChunk(root); LegacyWorldDataHook.prepareLegacyEntities(root); - CompoundTag fixed = NbtUtils.update(DataFixers.getDataFixer(), DataFixTypes.CHUNK, - root, version, SharedConstants.getCurrentVersion().getWorldVersion()); + CompoundTag fixed = DataFixTypes.CHUNK.update(DataFixers.getDataFixer(), + root, version, SharedConstants.getCurrentVersion().getDataVersion().getVersion()); return LegacyWorldDataHook.finalizeLegacyChunk(fixed); } @@ -210,8 +210,8 @@ private static CompoundTag convert(CompoundTag root, int version) { LegacyWorldDataHook.prepareLegacyChunk(root); if (!baseFirst) basePrepare.invoke(null, root); LegacyWorldDataHook.prepareLegacyEntities(root); - root = NbtUtils.update(DataFixers.getDataFixer(), DataFixTypes.CHUNK, - root, 1343, SharedConstants.getCurrentVersion().getWorldVersion()); + root = DataFixTypes.CHUNK.update(DataFixers.getDataFixer(), + root, 1343, SharedConstants.getCurrentVersion().getDataVersion().getVersion()); root = LegacyWorldDataHook.finalizeLegacyChunk(root); CompoundTag actual = root.getList("entities", 10).getCompound(0) .getList("Items", 10).getCompound(0); @@ -246,7 +246,7 @@ private static CompoundTag convert(CompoundTag root, int version) { } @Test public void detachesPendingChunkNbtIncludingNestedMapsAndArrays() { - CompoundTag pending = chunk(entity("example:vehicle"), SharedConstants.getCurrentVersion().getWorldVersion()); + CompoundTag pending = chunk(entity("example:vehicle"), SharedConstants.getCurrentVersion().getDataVersion().getVersion()); pending.getCompound("Level").putIntArray("Biomes", new int[] {1, 2, 3}); CompoundTag before = pending.copy(); CompoundTag read = LegacyWorldDataHook.copyChunkForMigration(pending); diff --git a/src/test/java/zone/moddev/mc/basepower/migration/LegacyEntityAttributeAliasesTest.java b/src/test/java/zone/moddev/mc/basepower/migration/LegacyEntityAttributeAliasesTest.java index 8fb6966e..8f03fe4e 100644 --- a/src/test/java/zone/moddev/mc/basepower/migration/LegacyEntityAttributeAliasesTest.java +++ b/src/test/java/zone/moddev/mc/basepower/migration/LegacyEntityAttributeAliasesTest.java @@ -28,9 +28,6 @@ import net.minecraft.world.level.Level; import net.minecraft.server.level.ServerLevel; import net.minecraftforge.common.ForgeMod; -import net.minecraftforge.event.RegistryEvent; -import net.minecraftforge.registries.DeferredRegister; -import net.minecraftforge.registries.ForgeRegistries; import org.junit.BeforeClass; import org.junit.Test; @@ -119,12 +116,12 @@ public class LegacyEntityAttributeAliasesTest { @Test public void modernChunksAndPlayersRemainUnchanged() { CompoundTag root = chunk(entity("minecraft:wolf", true)); - root.putInt("DataVersion", SharedConstants.getCurrentVersion().getWorldVersion()); + root.putInt("DataVersion", SharedConstants.getCurrentVersion().getDataVersion().getVersion()); CompoundTag before = root.copy(); LegacyEntityDataFixes.prepare(root); assertEquals(before, root); CompoundTag player = player(entity("minecraft:wolf", true)); CompoundTag result = LegacyPlayerDataFixes.update(DataFixers.getDataFixer(), DataFixTypes.PLAYER, - player, SharedConstants.getCurrentVersion().getWorldVersion()); + player, SharedConstants.getCurrentVersion().getDataVersion().getVersion()); assertEquals(player, result); } @@ -239,7 +236,7 @@ public class LegacyEntityAttributeAliasesTest { source.putFloat("Health", 8F); CompoundTag red = (CompoundTag) DataFixers.getDataFixer().update(References.ENTITY, new Dynamic<>(NbtOps.INSTANCE, source.copy()), 1343, - SharedConstants.getCurrentVersion().getWorldVersion()).getValue(); + SharedConstants.getCurrentVersion().getDataVersion().getVersion()).getValue(); Wolf before = new Wolf(EntityType.WOLF, world); before.load(red); assertEquals(1D, before.getAttribute(ForgeMod.SWIM_SPEED.get()).getBaseValue(), 0D); CompoundTag root = chunk(source); @@ -293,10 +290,7 @@ private static CompoundTag player(CompoundTag entity) { } private static void registerForgeAttributes() throws Exception { - if (ForgeMod.SWIM_SPEED.isPresent()) return; - Field attributes = ForgeMod.class.getDeclaredField("ATTRIBUTES"); attributes.setAccessible(true); - Method register = DeferredRegister.class.getDeclaredMethod("addEntries", RegistryEvent.Register.class); register.setAccessible(true); - register.invoke(attributes.get(null), new RegistryEvent.Register<>(ForgeRegistries.ATTRIBUTES.getRegistryName(), ForgeRegistries.ATTRIBUTES)); + LegacyForgeAttributeRegistryTest.registerForgeAttributes(); } private static void set(Class owner, Object instance, String name, Object value) throws Exception { diff --git a/src/test/java/zone/moddev/mc/basepower/migration/LegacyForgeAttributeRegistryTest.java b/src/test/java/zone/moddev/mc/basepower/migration/LegacyForgeAttributeRegistryTest.java index 140185c6..662ade61 100644 --- a/src/test/java/zone/moddev/mc/basepower/migration/LegacyForgeAttributeRegistryTest.java +++ b/src/test/java/zone/moddev/mc/basepower/migration/LegacyForgeAttributeRegistryTest.java @@ -23,11 +23,12 @@ import net.minecraft.world.level.Level; import net.minecraft.server.level.ServerLevel; import net.minecraftforge.common.ForgeMod; -import net.minecraftforge.event.RegistryEvent; -import net.minecraftforge.registries.DeferredRegister; +import net.minecraft.resources.ResourceLocation; import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.IForgeRegistry; import org.junit.BeforeClass; import org.junit.Test; +import zone.moddev.mc.advantageworks.unit.ForgeUnitRegistries; public class LegacyForgeAttributeRegistryTest { private static final String[][] ALIASES = { @@ -39,13 +40,21 @@ public class LegacyForgeAttributeRegistryTest { @BeforeClass public static void bootstrap() throws Exception { Bootstrap.bootStrap(); + registerForgeAttributes(); + } + + static void registerForgeAttributes() throws ReflectiveOperationException { + ForgeUnitRegistries.registerVanillaFluidTypes(); if (ForgeMod.SWIM_SPEED.isPresent()) return; - Field field = ForgeMod.class.getDeclaredField("ATTRIBUTES"); field.setAccessible(true); - Method register = DeferredRegister.class.getDeclaredMethod("addEntries", RegistryEvent.Register.class); - register.setAccessible(true); - register.invoke(field.get(null), new RegistryEvent.Register<>(ForgeRegistries.ATTRIBUTES.getRegistryName(), ForgeRegistries.ATTRIBUTES)); + ForgeUnitRegistries.registerForgeEntries("ATTRIBUTES", ForgeRegistries.Keys.ATTRIBUTES, ForgeRegistries.ATTRIBUTES); + Method alias = ForgeMod.class.getDeclaredMethod("addAlias", IForgeRegistry.class, + ResourceLocation.class, ResourceLocation.class); + alias.setAccessible(true); + alias.invoke(null, ForgeRegistries.ATTRIBUTES, + new ResourceLocation("forge:reach_distance"), new ResourceLocation("forge:block_reach")); } + @Test public void retainsEveryForgeAliasAndForeignPayloadBeforeNativeDfu() { for (String[] alias : ALIASES) { CompoundTag source = entity(alias[0], 7.5D), before = source.copy(); @@ -70,7 +79,7 @@ public class LegacyForgeAttributeRegistryTest { CompoundTag source = entity(ALIASES[index][0], values[index]); source.putString("id", "minecraft:wolf"); CompoundTag raw = (CompoundTag) DataFixers.getDataFixer().update(References.ENTITY, new Dynamic<>(NbtOps.INSTANCE, source.copy()), 1343, - SharedConstants.getCurrentVersion().getWorldVersion()).getValue(); + SharedConstants.getCurrentVersion().getDataVersion().getVersion()).getValue(); Wolf negative = new Wolf(EntityType.WOLF, world); negative.load(raw); AttributeInstance old = negative.getAttribute(ForgeRegistries.ATTRIBUTES.getValue( new net.minecraft.resources.ResourceLocation(ALIASES[index][1]))); @@ -149,7 +158,7 @@ public class LegacyForgeAttributeRegistryTest { @Test public void modernChunksAndSimilarUnknownNamesRemainUntouched() { for (String[] alias : ALIASES) { CompoundTag root = chunk(entity(alias[0], 7D)); - root.putInt("DataVersion", SharedConstants.getCurrentVersion().getWorldVersion()); + root.putInt("DataVersion", SharedConstants.getCurrentVersion().getDataVersion().getVersion()); CompoundTag before = root.copy(); LegacyEntityDataFixes.prepare(root); assertEquals(before, root); root = chunk(entity(alias[0] + ".private", 7D)); before = root.copy(); LegacyEntityDataFixes.prepare(root); LegacyEntityDataFixes.finish(root); assertEquals(before, root); diff --git a/src/test/java/zone/moddev/mc/basepower/migration/LegacyMachineItemsTest.java b/src/test/java/zone/moddev/mc/basepower/migration/LegacyMachineItemsTest.java index dfbe67c0..f76ba907 100644 --- a/src/test/java/zone/moddev/mc/basepower/migration/LegacyMachineItemsTest.java +++ b/src/test/java/zone/moddev/mc/basepower/migration/LegacyMachineItemsTest.java @@ -96,7 +96,7 @@ public class LegacyMachineItemsTest { @Test public void currentVersionMachineDataIsNotRewritten() { CompoundTag root = chunk(tile(stack("minecraft:red_wool", 0, 7))); - root.putInt("DataVersion", SharedConstants.getCurrentVersion().getWorldVersion()); + root.putInt("DataVersion", SharedConstants.getCurrentVersion().getDataVersion().getVersion()); CompoundTag expected = root.copy(); LegacyWorldDataHook.prepareLegacyEntities(root); assertEquals(expected, root); @@ -319,8 +319,8 @@ private static CompoundTag convertedTile(CompoundTag item) { private static CompoundTag convert(CompoundTag root, int version) { LegacyWorldDataHook.prepareLegacyChunk(root); LegacyWorldDataHook.prepareLegacyEntities(root); - CompoundTag fixed = NbtUtils.update(DataFixers.getDataFixer(), DataFixTypes.CHUNK, - root, version, SharedConstants.getCurrentVersion().getWorldVersion()); + CompoundTag fixed = DataFixTypes.CHUNK.update(DataFixers.getDataFixer(), + root, version, SharedConstants.getCurrentVersion().getDataVersion().getVersion()); return LegacyWorldDataHook.finalizeLegacyChunk(fixed); } diff --git a/src/test/java/zone/moddev/mc/basepower/migration/LegacyPlayerDataFixesTest.java b/src/test/java/zone/moddev/mc/basepower/migration/LegacyPlayerDataFixesTest.java index 5f8a523e..363e4ce2 100644 --- a/src/test/java/zone/moddev/mc/basepower/migration/LegacyPlayerDataFixesTest.java +++ b/src/test/java/zone/moddev/mc/basepower/migration/LegacyPlayerDataFixesTest.java @@ -122,7 +122,7 @@ public class LegacyPlayerDataFixesTest { } @Test public void usesThePassedFixerAndNativePlayerTypeWithAnExplicit704Boundary() { - int current = SharedConstants.getCurrentVersion().getWorldVersion(); + int current = SharedConstants.getCurrentVersion().getDataVersion().getVersion(); for (int source : new int[] {-1, 512, 703, 704, 1343, 2230}) { RecordingFixer fixer = new RecordingFixer(root -> {}); LegacyPlayerDataFixes.update(fixer, DataFixTypes.PLAYER, player("Inventory", stack("minecraft:stone", 0)), source); @@ -133,7 +133,7 @@ public class LegacyPlayerDataFixesTest { } @Test public void currentOrNewerDataIsCopiedWithoutOldVersionDfu() { - for (int version : new int[] {SharedConstants.getCurrentVersion().getWorldVersion(), Integer.MAX_VALUE}) { + for (int version : new int[] {SharedConstants.getCurrentVersion().getDataVersion().getVersion(), Integer.MAX_VALUE}) { CompoundTag source = player("Inventory", stack("minecraft:diamond_pickaxe", 17)); RecordingFixer fixer = new RecordingFixer(root -> fail("Current data must not be reinterpreted")); CompoundTag result = LegacyPlayerDataFixes.update(fixer, DataFixTypes.PLAYER, source, version); diff --git a/src/test/java/zone/moddev/mc/basepower/migration/LegacyPlayerTransformerTest.java b/src/test/java/zone/moddev/mc/basepower/migration/LegacyPlayerTransformerTest.java index 5a9ba745..6572bdb6 100644 --- a/src/test/java/zone/moddev/mc/basepower/migration/LegacyPlayerTransformerTest.java +++ b/src/test/java/zone/moddev/mc/basepower/migration/LegacyPlayerTransformerTest.java @@ -21,10 +21,11 @@ import static org.junit.Assert.*; public class LegacyPlayerTransformerTest { - private static final String NATIVE = "net/minecraft/nbt/NbtUtils"; + private static final String NATIVE = "net/minecraft/util/datafix/DataFixTypes"; private static final String WRAPPER = "zone/moddev/mc/basepower/migration/LegacyPlayerDataFixes"; - private static final String DESC = "(Lcom/mojang/datafixers/DataFixer;Lnet/minecraft/util/datafix/DataFixTypes;" + private static final String DESC = "(Lcom/mojang/datafixers/DataFixer;" + "Lnet/minecraft/nbt/CompoundTag;I)Lnet/minecraft/nbt/CompoundTag;"; + private static final String WRAPPER_DESC = "(L" + NATIVE + ";" + DESC.substring(1); private static final String[][] TARGETS = {{"PlayerData", "legacy_player_file"}, {"ServerWorldInfo", "legacy_embedded_player"}}; @Test public void exactNativeLoosePlayerLoadAndEmbeddedUpdateArePatched() throws Exception { @@ -132,7 +133,8 @@ private static MethodNode containing(ClassNode node) { for (MethodNode method : node.methods) for (AbstractInsnNode instruction : method.instructions.toArray()) { if (instruction instanceof MethodInsnNode) { MethodInsnNode call = (MethodInsnNode) instruction; - if ((call.owner.equals(NATIVE) || call.owner.equals(WRAPPER)) && call.desc.equals(DESC)) return method; + if ((call.owner.equals(NATIVE) && call.desc.equals(DESC)) + || (call.owner.equals(WRAPPER) && call.desc.equals(WRAPPER_DESC))) return method; } } throw new AssertionError("No player update call"); @@ -151,9 +153,10 @@ private static int calls(ClassNode node, String owner) { for (MethodNode method : node.methods) for (AbstractInsnNode instruction : method.instructions.toArray()) { if (instruction instanceof MethodInsnNode) { MethodInsnNode call = (MethodInsnNode) instruction; - if (call.owner.equals(owner) && call.desc.equals(DESC)) { - assertEquals(Opcodes.INVOKESTATIC, call.getOpcode()); - if (owner.equals(WRAPPER)) assertEquals("update", call.name); + boolean wrapper = owner.equals(WRAPPER); + if (call.owner.equals(owner) && call.desc.equals(wrapper ? WRAPPER_DESC : DESC)) { + assertEquals(wrapper ? Opcodes.INVOKESTATIC : Opcodes.INVOKEVIRTUAL, call.getOpcode()); + if (wrapper) assertEquals("updateNativePlayer", call.name); count++; } } @@ -185,7 +188,7 @@ private static Object embeddedInfo(Class type, CompoundTag source, int versio if (types[i] == net.minecraft.world.level.LevelSettings.class) { args[i] = new net.minecraft.world.level.LevelSettings("Test", net.minecraft.world.level.GameType.SURVIVAL, false, net.minecraft.world.Difficulty.NORMAL, false, new net.minecraft.world.level.GameRules(), - net.minecraft.world.level.DataPackConfig.DEFAULT); + net.minecraft.world.level.WorldDataConfiguration.DEFAULT); } } args[0] = DataFixers.getDataFixer(); @@ -204,7 +207,7 @@ private static void rejected(ClassNode node, String target) throws Exception { private static void transform(ClassNode node, String target) throws Exception { ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); - assertNotNull("Use the Java 8 test toolchain", engine); + assertNotNull("Transformer tests require Forge's bundled Nashorn engine", engine); try (InputStreamReader reader = new InputStreamReader(resource("/coremods/poweradvantage_118_migration.js"), StandardCharsets.UTF_8)) { engine.eval(reader); } diff --git a/src/test/java/zone/moddev/mc/basepower/migration/LegacyTransformerOrderTest.java b/src/test/java/zone/moddev/mc/basepower/migration/LegacyTransformerOrderTest.java index 145739ca..a89212d8 100644 --- a/src/test/java/zone/moddev/mc/basepower/migration/LegacyTransformerOrderTest.java +++ b/src/test/java/zone/moddev/mc/basepower/migration/LegacyTransformerOrderTest.java @@ -43,7 +43,7 @@ private static void check(boolean baseFirst, boolean productionNames) throws Exc if (target[1].equals("legacy_chunk_status")) { assertEquals(1, calls(node, "basepower", "finalizeLegacyChunk")); assertEquals(1, calls(node, "basemetals", "finalizeLegacyChunk")); - assertEquals(1, calls(node, "basepower", "copyChunkForMigration")); + assertEquals(1, calls(node, "basepower", "copyChunkReadForMigration")); assertEquals(1, calls(node, "basepower", "prepareLegacyEntities")); assertEntityPreparationOrder(node); } diff --git a/src/test/java/zone/moddev/mc/basepower/migration/LegacyWorldMetadataTest.java b/src/test/java/zone/moddev/mc/basepower/migration/LegacyWorldMetadataTest.java index 0c97f013..91424dd0 100644 --- a/src/test/java/zone/moddev/mc/basepower/migration/LegacyWorldMetadataTest.java +++ b/src/test/java/zone/moddev/mc/basepower/migration/LegacyWorldMetadataTest.java @@ -1,6 +1,7 @@ package zone.moddev.mc.basepower.migration; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import java.util.Set; @@ -12,6 +13,25 @@ import org.junit.Test; public class LegacyWorldMetadataTest { + @Test public void movesTheOldPaintingRegistryWithoutLosingItsEntries() { + CompoundTag root = new CompoundTag(), forge = new CompoundTag(), registries = new CompoundTag(); + CompoundTag snapshot = new CompoundTag(), entry = new CompoundTag(); + ListTag ids = new ListTag(); + entry.putString("K", "example:custom_painting"); + entry.putInt("V", 42); + ids.add(entry); + snapshot.put("ids", ids); + registries.put("minecraft:motive", snapshot); + forge.put("Registries", registries); + root.put("fml", forge); + LegacyWorldMetadata.remap(root, id -> false); + assertFalse(registries.contains("minecraft:motive")); + assertEquals(snapshot, registries.getCompound("minecraft:painting_variant")); + CompoundTag once = root.copy(); + LegacyWorldMetadata.remap(root, id -> false); + assertEquals(once, root); + } + @BeforeClass public static void registerAliases() { AdvantageLegacyMigrations.registerPowerAdvantage(); LegacyMigrationApi.registerNamespaceAlias("test_old_expansion", "test_new_expansion"); diff --git a/src/test/java/zone/moddev/mc/basepower/migration/VanillaRegistryMappingEventTest.java b/src/test/java/zone/moddev/mc/basepower/migration/VanillaRegistryMappingEventTest.java index 02340093..9ec9bf12 100644 --- a/src/test/java/zone/moddev/mc/basepower/migration/VanillaRegistryMappingEventTest.java +++ b/src/test/java/zone/moddev/mc/basepower/migration/VanillaRegistryMappingEventTest.java @@ -3,8 +3,9 @@ import java.lang.reflect.Proxy; import java.util.Collections; import net.minecraft.resources.ResourceLocation; -import net.minecraftforge.event.RegistryEvent.MissingMappings; -import net.minecraftforge.registries.ForgeRegistryEntry; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; +import net.minecraftforge.registries.MissingMappingsEvent; import net.minecraftforge.registries.IForgeRegistry; import org.junit.Test; import static org.junit.Assert.*; @@ -16,50 +17,55 @@ public class VanillaRegistryMappingEventTest { @Test public void resolvesTheTargetFromActiveNotTheIncompleteStagingRegistry() { Entry target = new Entry(); IForgeRegistry staging = registry(null, false), active = registry(target, true); - MissingMappings.Mapping mapping = new MissingMappings.Mapping<>(staging, active, SOURCE, 42); + MissingMappingsEvent.Mapping mapping = new MissingMappingsEvent.Mapping<>(staging, active, SOURCE, 42); VanillaRegistryRemaps.remap(event(staging, mapping), active); - assertEquals(MissingMappings.Action.REMAP, mapping.getAction()); - assertSame(target, mapping.getTarget()); - assertEquals(42, mapping.id); + assertEquals(MissingMappingsEvent.Action.REMAP, property(mapping, "action")); + assertSame(target, property(mapping, "target")); + assertEquals(42, mapping.getId()); } - @Test public void respectsAnExistingMappingDecision() { + @Test public void aLaterExplicitForgeDecisionOverridesTheCompatibilityMapping() { IForgeRegistry active = registry(new Entry(), true); - MissingMappings.Mapping mapping = new MissingMappings.Mapping<>(active, active, SOURCE, 42); - mapping.ignore(); - VanillaRegistryRemaps.remap(event(active, mapping), active); - assertEquals(MissingMappings.Action.IGNORE, mapping.getAction()); + MissingMappingsEvent.Mapping mapping = new MissingMappingsEvent.Mapping<>(active, active, SOURCE, 42); + var bus = net.minecraftforge.eventbus.api.BusBuilder.builder().build(); + bus.addListener(net.minecraftforge.eventbus.api.EventPriority.HIGHEST, + (MissingMappingsEvent event) -> VanillaRegistryRemaps.remap(event, active)); + bus.addListener(net.minecraftforge.eventbus.api.EventPriority.NORMAL, + (MissingMappingsEvent event) -> mapping.ignore()); + bus.start(); + bus.post(event(active, mapping)); + assertEquals(MissingMappingsEvent.Action.IGNORE, property(mapping, "action")); } @Test public void leavesUnrecognizedEntriesUnresolvedForForgeToReport() { IForgeRegistry active = registry(new Entry(), true); - MissingMappings.Mapping mapping = new MissingMappings.Mapping<>(active, active, + MissingMappingsEvent.Mapping mapping = new MissingMappingsEvent.Mapping<>(active, active, new ResourceLocation("othermod:zombie_pigman_spawn_egg"), 42); VanillaRegistryRemaps.remap(event(active, mapping), active); - assertEquals(MissingMappings.Action.DEFAULT, mapping.getAction()); + assertEquals(MissingMappingsEvent.Action.DEFAULT, property(mapping, "action")); } @Test public void neverSubstitutesARegistryDefaultForAMissingTarget() { IForgeRegistry absent = registry(new Entry(), false); - MissingMappings.Mapping mapping = new MissingMappings.Mapping<>(absent, absent, SOURCE, 42); + MissingMappingsEvent.Mapping mapping = new MissingMappingsEvent.Mapping<>(absent, absent, SOURCE, 42); try { VanillaRegistryRemaps.remap(event(absent, mapping), absent); fail("A registry default must not replace the missing item"); } catch (IllegalStateException expected) { - assertEquals(MissingMappings.Action.DEFAULT, mapping.getAction()); + assertEquals(MissingMappingsEvent.Action.DEFAULT, property(mapping, "action")); } } @Test public void retiresOnlyTheTwoNonSerializedVanillaMemoryIndexes() { - IForgeRegistry active = registry(null, false); + IForgeRegistry active = registry(null, false, "minecraft:memory_module_type"); for (String name : new String[] {"golem_last_seen_time", "opened_doors"}) { - MissingMappings.Mapping mapping = new MissingMappings.Mapping<>(active, active, + MissingMappingsEvent.Mapping mapping = new MissingMappingsEvent.Mapping<>(active, active, new ResourceLocation("minecraft", name), 42); - MissingMappings event = new MissingMappings<>(new ResourceLocation("minecraft:memory_module_type"), + MissingMappingsEvent event = new MissingMappingsEvent(key("minecraft:memory_module_type"), active, Collections.singletonList(mapping)); VanillaRegistryRemaps.remap(event, active); - assertEquals(MissingMappings.Action.IGNORE, mapping.getAction()); - assertNull(mapping.getTarget()); + assertEquals(MissingMappingsEvent.Action.IGNORE, property(mapping, "action")); + assertNull(property(mapping, "target")); } } @@ -71,16 +77,48 @@ public class VanillaRegistryMappingEventTest { new ResourceLocation("minecraft:opened_doors"))); } - private static MissingMappings event(IForgeRegistry staging, MissingMappings.Mapping mapping) { - return new MissingMappings<>(new ResourceLocation("minecraft:item"), staging, Collections.singletonList(mapping)); + @Test public void retiresOnlyTheTwoPointsOfInterestRemovedByVanilla() { + IForgeRegistry active = registry(null, false, "minecraft:point_of_interest_type"); + for (String id : new String[] {"minecraft:nitwit", "minecraft:unemployed", "minecraft:home", "othermod:nitwit"}) { + var mapping = new MissingMappingsEvent.Mapping<>(active, active, new ResourceLocation(id), 42); + VanillaRegistryRemaps.remap(event(active, mapping), active); + boolean retired = id.equals("minecraft:nitwit") || id.equals("minecraft:unemployed"); + assertEquals(id, retired ? MissingMappingsEvent.Action.IGNORE : MissingMappingsEvent.Action.DEFAULT, + property(mapping, "action")); + assertNull(property(mapping, "target")); + } + } + + private static MissingMappingsEvent event(IForgeRegistry staging, MissingMappingsEvent.Mapping mapping) { + return new MissingMappingsEvent(staging.getRegistryKey(), staging, Collections.singletonList(mapping)); + } + + private static ResourceKey> key(String id) { + return ResourceKey.createRegistryKey(new ResourceLocation(id)); + } + + // Forge keeps the result fields package-private for its snapshot loader. + private static Object property(MissingMappingsEvent.Mapping mapping, String name) { + try { + var field = mapping.getClass().getDeclaredField(name); + field.setAccessible(true); + return field.get(mapping); + } catch (ReflectiveOperationException failure) { + throw new AssertionError(failure); + } } - @SuppressWarnings("unchecked") private static IForgeRegistry registry(Entry target, boolean contains) { + return registry(target, contains, "minecraft:item"); + } + + @SuppressWarnings("unchecked") + private static IForgeRegistry registry(Entry target, boolean contains, String id) { return (IForgeRegistry) Proxy.newProxyInstance(IForgeRegistry.class.getClassLoader(), new Class[] {IForgeRegistry.class}, (proxy, method, args) -> { switch (method.getName()) { - case "getRegistrySuperType": return Entry.class; + case "getRegistryKey": return key(id); + case "getRegistryName": return new ResourceLocation(id); case "containsKey": return contains && TARGET.equals(args[0]); case "getValue": return target; case "getKey": return args[0] == target ? TARGET : null; @@ -89,5 +127,5 @@ private static IForgeRegistry registry(Entry target, boolean contains) { }); } - private static final class Entry extends ForgeRegistryEntry {} + private static final class Entry {} } diff --git a/src/test/java/zone/moddev/mc/basepower/recipe/MaterialPolicyPackTest.java b/src/test/java/zone/moddev/mc/basepower/recipe/MaterialPolicyPackTest.java index 74be58b5..492e85fa 100644 --- a/src/test/java/zone/moddev/mc/basepower/recipe/MaterialPolicyPackTest.java +++ b/src/test/java/zone/moddev/mc/basepower/recipe/MaterialPolicyPackTest.java @@ -4,6 +4,8 @@ import com.google.gson.JsonParser; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.util.HashSet; +import java.util.Set; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.PackType; import org.junit.Test; @@ -40,19 +42,23 @@ public class MaterialPolicyPackTest { @Test public void policyOnlySuppliesServerDataAndHonoursResourceDiscovery() { MaterialPolicyPack pack = new MaterialPolicyPack(true, false); ResourceLocation rubber = new ResourceLocation("forge", "tags/items/rubber.json"); - assertTrue(pack.hasResource(PackType.SERVER_DATA, rubber)); - assertFalse(pack.hasResource(PackType.CLIENT_RESOURCES, rubber)); - assertEquals(3, pack.getResources(PackType.SERVER_DATA, "forge", "tags/items", 99, - name -> name.endsWith(".json")).size()); - assertTrue(pack.getResources(PackType.SERVER_DATA, "basepower", "tags/items", 99, - name -> true).isEmpty()); - assertTrue(pack.getResources(PackType.CLIENT_RESOURCES, "forge", "tags/items", 99, - name -> true).isEmpty()); + assertNotNull(pack.getResource(PackType.SERVER_DATA, rubber)); + assertNull(pack.getResource(PackType.CLIENT_RESOURCES, rubber)); + assertEquals(Set.of(rubber, new ResourceLocation("forge:tags/items/ingots/rubber.json"), + new ResourceLocation("forge:tags/items/solder.json")), list(pack, PackType.SERVER_DATA, "forge")); + assertTrue(list(pack, PackType.SERVER_DATA, "basepower").isEmpty()); + assertTrue(list(pack, PackType.CLIENT_RESOURCES, "forge").isEmpty()); + } + + private static Set list(MaterialPolicyPack pack, PackType type, String namespace) { + Set entries = new HashSet<>(); + pack.listResources(type, namespace, "tags/items", (id, supplier) -> entries.add(id)); + return entries; } private static JsonObject read(MaterialPolicyPack pack, String path) throws Exception { try (InputStream stream = pack.getResource(PackType.SERVER_DATA, - new ResourceLocation("forge", "tags/items/" + path + ".json"))) { + new ResourceLocation("forge", "tags/items/" + path + ".json")).get()) { return new JsonParser().parse(new String(stream.readAllBytes(), StandardCharsets.UTF_8)).getAsJsonObject(); } } diff --git a/src/test/java/zone/moddev/mc/basepower/recipe/MaterialTagsTest.java b/src/test/java/zone/moddev/mc/basepower/recipe/MaterialTagsTest.java index afab5c5f..5dfe0aa0 100644 --- a/src/test/java/zone/moddev/mc/basepower/recipe/MaterialTagsTest.java +++ b/src/test/java/zone/moddev/mc/basepower/recipe/MaterialTagsTest.java @@ -1,16 +1,19 @@ package zone.moddev.mc.basepower.recipe; +import com.google.gson.JsonObject; import com.google.gson.JsonParser; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.Map; +import java.util.List; +import java.util.Set; import net.minecraft.world.item.Item; import net.minecraft.world.item.Items; -import net.minecraft.tags.Tag; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.PackType; import org.junit.BeforeClass; import org.junit.Test; +import zone.moddev.mc.basepower.resources.NativeTestTags; import static org.junit.Assert.*; public class MaterialTagsTest { @@ -18,42 +21,32 @@ public class MaterialTagsTest { @Test public void addsNativeAliasesWithoutReplacingUserEntries() throws Exception { MaterialPolicyPack pack = new MaterialPolicyPack(true, true); - Tag rubber = resolve(pack, "rubber", "[\"minecraft:stick\",\"basepower:bioplastic_ingot\"]"); - assertTrue(rubber.getValues().contains(Items.STICK)); - assertTrue(rubber.getValues().contains(Items.APPLE)); - assertEquals(2, rubber.getValues().size()); - assertTrue(resolve(pack, "ingots/rubber", "[]").getValues().contains(Items.APPLE)); - Tag solder = resolve(pack, "solder", "[\"minecraft:redstone\"]"); - assertTrue(solder.getValues().contains(Items.IRON_INGOT)); - assertTrue(solder.getValues().contains(Items.GOLD_INGOT)); - assertTrue(solder.getValues().contains(Items.REDSTONE)); - assertEquals(3, solder.getValues().size()); + assertEquals(Set.of(Items.STICK, Items.APPLE), + resolve(pack, "rubber", "[\"minecraft:stick\",\"basepower:bioplastic_ingot\"]")); + assertTrue(resolve(pack, "ingots/rubber", "[]").contains(Items.APPLE)); + assertEquals(Set.of(Items.IRON_INGOT, Items.GOLD_INGOT, Items.REDSTONE), + resolve(pack, "solder", "[\"minecraft:redstone\"]")); } @Test public void disabledOptionsDoNotAddAliases() throws Exception { MaterialPolicyPack pack = new MaterialPolicyPack(false, false); - Tag rubber = resolve(pack, "rubber", "[\"minecraft:stick\"]"); - assertTrue(rubber.getValues().contains(Items.STICK)); - assertFalse(rubber.getValues().contains(Items.APPLE)); - assertTrue(resolve(pack, "solder", "[]").getValues().isEmpty()); + assertEquals(Set.of(Items.STICK), resolve(pack, "rubber", "[\"minecraft:stick\"]")); + assertTrue(resolve(pack, "solder", "[]").isEmpty()); } - private static Tag resolve(MaterialPolicyPack pack, String path, String userEntries) throws Exception { + private static Set resolve(MaterialPolicyPack pack, String path, String userEntries) throws Exception { var resource = new ResourceLocation("forge", "tags/items/" + path + ".json"); - var parser = new JsonParser(); - var builder = Tag.Builder.tag(); - try (var reader = new InputStreamReader(pack.getResource(PackType.SERVER_DATA, resource), + JsonObject policy; + try (var reader = new InputStreamReader(pack.getResource(PackType.SERVER_DATA, resource).get(), StandardCharsets.UTF_8)) { - var json = parser.parse(reader).getAsJsonObject(); - assertFalse(json.get("replace").getAsBoolean()); - builder.addFromJson(json, pack.getName()); + policy = JsonParser.parseReader(reader).getAsJsonObject(); } - builder.addFromJson(parser.parse("{\"values\":" + userEntries + "}").getAsJsonObject(), "user"); + assertFalse(policy.get("replace").getAsBoolean()); Map items = Map.of("minecraft:stick", Items.STICK, "minecraft:redstone", Items.REDSTONE, "basepower:bioplastic_ingot", Items.APPLE); - Map> tags = Map.of( - "forge:ingots/lead", new Tag<>(java.util.List.of(Items.IRON_INGOT)), - "forge:ingots/tin", new Tag<>(java.util.List.of(Items.GOLD_INGOT))); - return builder.build(id -> tags.get(id.toString()), id -> items.get(id.toString())).right().get(); + Map> tags = Map.of( + "forge:ingots/lead", List.of(Items.IRON_INGOT), "forge:ingots/tin", List.of(Items.GOLD_INGOT)); + return NativeTestTags.resolve(id -> items.get(id.toString()), id -> tags.get(id.toString()), + policy, JsonParser.parseString("{\"values\":" + userEntries + "}")); } } diff --git a/src/test/java/zone/moddev/mc/basepower/registry/MachineGuiTitleTest.java b/src/test/java/zone/moddev/mc/basepower/registry/MachineGuiTitleTest.java index 8f403e25..593c648b 100644 --- a/src/test/java/zone/moddev/mc/basepower/registry/MachineGuiTitleTest.java +++ b/src/test/java/zone/moddev/mc/basepower/registry/MachineGuiTitleTest.java @@ -4,8 +4,8 @@ import net.minecraft.world.level.block.entity.ChestBlockEntity; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.network.chat.TextComponent; -import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.contents.TranslatableContents; import org.junit.BeforeClass; import org.junit.Test; import static org.junit.Assert.*; @@ -16,18 +16,22 @@ public class MachineGuiTitleTest { } @Test public void defaultTitleUsesTheBlockTranslationInsteadOfAnInternalInventoryName() { - assertEquals("block.minecraft.chest", ((TranslatableComponent) MachineGUIRegistry.menuTitle( - new ChestBlockEntity(net.minecraft.core.BlockPos.ZERO, Blocks.CHEST.defaultBlockState()), Blocks.CHEST)).getKey()); + assertEquals("block.minecraft.chest", key(MachineGUIRegistry.menuTitle( + new ChestBlockEntity(net.minecraft.core.BlockPos.ZERO, Blocks.CHEST.defaultBlockState()), Blocks.CHEST))); } @Test public void renamedMachinesKeepThePlayersFullName() { ChestBlockEntity tile = new ChestBlockEntity(net.minecraft.core.BlockPos.ZERO, Blocks.CHEST.defaultBlockState()); - tile.setCustomName(new TextComponent("John's oil tank")); + tile.setCustomName(Component.literal("John's oil tank")); assertEquals("John's oil tank", MachineGUIRegistry.menuTitle(tile, Blocks.CHEST).getString()); } @Test public void unnamedTilesWithoutInventoryNamesStillHaveATranslatableTitle() { BlockEntity tile = new BlockEntity(BlockEntityType.CHEST, net.minecraft.core.BlockPos.ZERO, Blocks.CHEST.defaultBlockState()) {}; - assertEquals("block.minecraft.chest", ((TranslatableComponent) MachineGUIRegistry.menuTitle(tile, Blocks.CHEST)).getKey()); + assertEquals("block.minecraft.chest", key(MachineGUIRegistry.menuTitle(tile, Blocks.CHEST))); + } + + private static String key(Component title) { + return ((TranslatableContents) title.getContents()).getKey(); } } diff --git a/src/test/java/zone/moddev/mc/basepower/resources/BuildWorkflowContractTest.java b/src/test/java/zone/moddev/mc/basepower/resources/BuildWorkflowContractTest.java index 75e7e323..3e0365a0 100644 --- a/src/test/java/zone/moddev/mc/basepower/resources/BuildWorkflowContractTest.java +++ b/src/test/java/zone/moddev/mc/basepower/resources/BuildWorkflowContractTest.java @@ -40,8 +40,8 @@ public class BuildWorkflowContractTest { assertTrue(name, policy >= 0 && checkout > policy); assertTrue(name, workflow.contains("ref: " + pins.getProperty("basemetals_source_commit"))); assertTrue(name, workflow.contains("BaseMetals-" + pins.getProperty("basemetals_version") + "-deobf.jar")); - assertTrue(name, workflow.contains("ref: d31f52f296739f34a0c13f554abf3e1ac071ae25")); - assertTrue(name, workflow.contains("master-1.18.2")); + assertTrue(name, workflow.contains("ref: 444d86ef48db2bf54422901b067c51d5832197fc")); + assertTrue(name, workflow.contains("master-1.19.4")); assertTrue(name, workflow.contains("java-version: '25.0.3+9.0.LTS'")); assertTrue(name, workflow.contains("java-version: '17.0.1+12'")); assertTrue(name, workflow.contains("java-version: '8.0.502+7'")); diff --git a/src/test/java/zone/moddev/mc/basepower/resources/CreativeTabInitializationTest.java b/src/test/java/zone/moddev/mc/basepower/resources/CreativeTabInitializationTest.java index f23a617d..d97b99e3 100644 --- a/src/test/java/zone/moddev/mc/basepower/resources/CreativeTabInitializationTest.java +++ b/src/test/java/zone/moddev/mc/basepower/resources/CreativeTabInitializationTest.java @@ -1,13 +1,41 @@ package zone.moddev.mc.basepower.resources; -import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import net.minecraft.network.chat.contents.TranslatableContents; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.Items; +import net.minecraftforge.event.CreativeModeTabEvent; import org.junit.Test; import zone.moddev.mc.basepower.init.ItemGroups; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; public class CreativeTabInitializationTest { - @Test public void addOnsCannotRaceTheSharedOneTimeTabInitialization() throws Exception { - assertTrue("All three constructors share one initialization lock", - Modifier.isSynchronized(ItemGroups.class.getMethod("init").getModifiers())); + @Test public void itemsFromBothSidesOfRegistrationShareOneNativeTabAndAppearOnce() throws Exception { + net.minecraft.SharedConstants.tryDetectVersion(); + net.minecraft.server.Bootstrap.bootStrap(); + assertSame(Items.STICK, ItemGroups.show(Items.STICK)); + List registered = new ArrayList<>(); + ItemGroups.register(new CreativeModeTabEvent.Register((setup, key, before, after) -> { + registered.add(key); + var builder = new CreativeModeTab.Builder(CreativeModeTab.Row.TOP, 0); + setup.accept(builder); + return builder.build(); + })); + ItemGroups.show(Items.IRON_INGOT); + ItemGroups.show(Items.STICK); + assertEquals(List.of(new ResourceLocation("basepower:machines")), registered); + var tab = ItemGroups.tab_powerAdvantage; + assertEquals("itemGroup.basepower", ((TranslatableContents) tab.getDisplayName().getContents()).getKey()); + var field = CreativeModeTab.class.getDeclaredField("displayItemsGenerator"); + field.setAccessible(true); + List entries = new ArrayList<>(); + ((CreativeModeTab.DisplayItemsGenerator) field.get(tab)).accept(null, + (stack, visibility) -> entries.add(stack.getItem())); + assertEquals(1, java.util.Collections.frequency(entries, Items.STICK)); + assertEquals(1, java.util.Collections.frequency(entries, Items.IRON_INGOT)); + assertFalse(entries.contains(Items.BEDROCK)); } } diff --git a/src/test/java/zone/moddev/mc/basepower/resources/NativeTagReaderContractTest.java b/src/test/java/zone/moddev/mc/basepower/resources/NativeTagReaderContractTest.java index 1118ef2a..a57a27ce 100644 --- a/src/test/java/zone/moddev/mc/basepower/resources/NativeTagReaderContractTest.java +++ b/src/test/java/zone/moddev/mc/basepower/resources/NativeTagReaderContractTest.java @@ -8,7 +8,6 @@ import java.nio.file.Paths; import net.minecraft.world.item.Item; import net.minecraft.world.item.Items; -import net.minecraft.tags.Tag; import net.minecraft.resources.ResourceLocation; import org.junit.BeforeClass; import org.junit.Test; @@ -26,12 +25,10 @@ public class NativeTagReaderContractTest { JsonObject json = new JsonParser().parse(new String(Files.readAllBytes(Paths.get( "src/main/resources/data/forge/tags/items/bars/steel.json")), StandardCharsets.UTF_8)).getAsJsonObject(); assertFalse("Do not silently ignore an obsolete optional array", json.has("optional")); - Tag.Builder builder = Tag.Builder.tag().addFromJson(json, "test"); - Tag absent = builder.build(id -> null, id -> null).right().get(); - assertTrue(absent.getValues().isEmpty()); - Tag present = builder.build(id -> null, id -> - id.equals(new ResourceLocation("basemetals:steel_bars")) ? Items.IRON_BARS : null).right().get(); - assertEquals(java.util.Collections.singletonList(Items.IRON_BARS), present.getValues()); + assertTrue(NativeTestTags.resolve(id -> null, id -> null, json).isEmpty()); + var present = NativeTestTags.resolve(id -> + id.equals(new ResourceLocation("basemetals:steel_bars")) ? Items.IRON_BARS : null, id -> null, json); + assertEquals(java.util.Set.of(Items.IRON_BARS), present); } @Test public void configuredAliasesUseANativeDataPackWithoutGlobalTagMutation() throws Exception { @@ -49,7 +46,7 @@ public class NativeTagReaderContractTest { assertNotEquals("net/minecraft/tags/SerializationTags", call.owner); assertNotEquals("net/minecraft/tags/StaticTags", call.owner); if (call.owner.equals("net/minecraft/server/packs/repository/Pack") - && call.name.equals("create")) { + && call.name.equals("readMetaAndCreate")) { policies++; } } diff --git a/src/test/java/zone/moddev/mc/basepower/resources/NativeTestTags.java b/src/test/java/zone/moddev/mc/basepower/resources/NativeTestTags.java new file mode 100644 index 00000000..de489e5c --- /dev/null +++ b/src/test/java/zone/moddev/mc/basepower/resources/NativeTestTags.java @@ -0,0 +1,35 @@ +package zone.moddev.mc.basepower.resources; + +import com.google.gson.JsonElement; +import com.mojang.serialization.JsonOps; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.function.Function; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagEntry; +import net.minecraft.tags.TagFile; + +import static org.junit.Assert.assertTrue; + +public final class NativeTestTags { + private NativeTestTags() {} + + public static Set resolve(Function element, + Function> tag, JsonElement... files) { + Set values = new LinkedHashSet<>(); + TagEntry.Lookup lookup = new TagEntry.Lookup<>() { + @Override public T element(ResourceLocation id) { return element.apply(id); } + @Override public Collection tag(ResourceLocation id) { return tag.apply(id); } + }; + for (JsonElement json : files) { + TagFile file = TagFile.CODEC.parse(JsonOps.INSTANCE, json) + .getOrThrow(false, message -> { throw new AssertionError(message); }); + if (file.replace()) values.clear(); + for (TagEntry entry : file.entries()) { + assertTrue("Unresolved required tag entry: " + entry, entry.build(lookup, values::add)); + } + } + return values; + } +} diff --git a/test-rig/NATIVE119.md b/test-rig/NATIVE119.md new file mode 100644 index 00000000..984c81e9 --- /dev/null +++ b/test-rig/NATIVE119.md @@ -0,0 +1,54 @@ +# Advantage Works on Minecraft 1.19.4 + +The 1.19.4 rig uses Java 17 and Forge 45.4.0. Use the pins in +[`profiles/native119/`](profiles/native119/) for the three mods, OreSpawn +and the local Base Metals migration build. The public Base Metals build +is not a substitute for that migration pin. + +## Build and inspect + +From the basePower checkout, build the three mods first, then run: + +```powershell +.\gradlew.bat -p test-rig check build runnerJar verifyHarnessIsolation +.\gradlew.bat -p test-rig verifyWorksNative119Inputs +.\gradlew.bat prepareCombinedEclipseLaunch +``` + +Refresh the Gradle project in Eclipse and use `runClientAllAdvantage.launch` +for all three mods, or `runClient.launch` for basePower alone. Use Java 17 +for the launches. Neither command opens a world. Make a separate world copy +for any manual upgrade test. + +For a disposable walkable Works server, run +`gradlew.bat -p test-rig runWorksPackagedCurrent`. Use the console +`stop` command to save and close it. The run folder records its inputs +and does not replace earlier results. + +## Automated checks + +The packaged profiles cover the same 21 stations with and without Base +Metals. The dedicated world-generation profiles check Advantage fallback +ores, Mineralogy coexistence and import of an existing OreSpawn profile. + +The scripts in `migration/` accept a Forge server seed, a sealed code +inventory, the inventory's SHA-256 and a hash-pinned jar profile. Seal an +installed server with `native119.py --seed --inventory ` +before using the migration runners. +Keep the source saves and their receipts outside the output directory. + +- `native119_legacy_world.py` and `native119_modern_upgrade.py` compare + saved and live state before operation and after a cold restart. +- `native119_broad.py` commissions every station; `native119_networks.py` + tests partial unload, bridge reconnects, longer drill runs and Forge + capabilities. +- `native119_recipes.py` checks the three recipe modes, ingredient sources + and unlocks. Its recipe-book exports also feed the + [graphical client check](client-resources/README.md). +- `native119_endurance.py` defaults to 30 minutes of measured operation. + Give it a passing migration receipt from the same jar profile with + `--retained ` to exercise a migrated world alongside the fresh one. + Use `audit_native119_endurance.py` to replay the station ledgers. + +Server success does not certify textures, GUI layouts or a real client's +login. Run the graphical check and keep manual acceptance separate. diff --git a/test-rig/build.gradle b/test-rig/build.gradle index b782592f..26d68d69 100644 --- a/test-rig/build.gradle +++ b/test-rig/build.gradle @@ -6,6 +6,9 @@ plugins { id 'net.minecraftforge.gradle' version '7.0.34' } +ext.forge45MavenizerDefault = '../../../../MinecraftMineralogy/OS119/OreSpawn/ci-fixtures/tools/minecraft-mavenizer-0.5.21-orespawn-compat.jar' +apply from: '../gradle/forge45-mavenizer.gradle' + // Saves, pinned inputs and evidence are siblings, never Gradle cleanup targets. layout.buildDirectory = layout.projectDirectory.dir('build/gradle') tasks.named('clean', Delete) { @@ -42,6 +45,7 @@ tasks.withType(AbstractArchiveTask).configureEach { sourceSets { main.java.srcDir 'shared-probe' + test.java.srcDir 'unit-support' // Preserve the 1.15 source oracle; its native 1.16 API copy runs instead. test.java.exclude '**/NativeHarvestIsolationTest.java' runner { @@ -66,7 +70,7 @@ repositories { } def powerCompileJar = file(project.findProperty('powerAdvantageDeobfJar') - ?: '../build/libs-dev/basePower-2.4.10.118021-deobf.jar') + ?: '../build/libs-dev/basePower-2.4.11.119041-deobf.jar') dependencies { implementation minecraft.dependency( @@ -101,14 +105,15 @@ tasks.register('verifyWorksCompileInputPin') { inputs.property('sha256', project.poweradvantage_deobf_sha256) inputs.property('sourceCommit', project.poweradvantage_source_commit) doLast { - if (!powerCompileJar.isFile()) throw new GradleException("Missing 1.18.2 Power compile input: ${powerCompileJar}") + if (!powerCompileJar.isFile()) throw new GradleException("Missing 1.19.4 Power compile input: ${powerCompileJar}") String actual = java.security.MessageDigest.getInstance('SHA-256') .digest(powerCompileJar.bytes).encodeHex().toString().toUpperCase(Locale.ROOT) if (actual != project.poweradvantage_deobf_sha256) { throw new GradleException("Power compile input changed: ${actual}; obtain a new parent pin") } - if (project.poweradvantage_source_commit != 'bab9971cc2d593eee1f4a3de6dafc7ec268d25c2') { - throw new GradleException('The harness requires the verified 1.18.2 Power source pin') + if (project.poweradvantage_source_commit != 'c9995315e2aee9d9bffec8eb8b1191ad8a0faeb7' + || project.poweradvantage_pin_status != 'SOURCE_AND_ARTIFACT_PINNED') { + throw new GradleException('The harness requires the verified 1.19.4 Power source pin') } } } @@ -147,6 +152,12 @@ tasks.named('jar', Jar) { } } +tasks.named('processRunnerResources', ProcessResources) { + from('src/main/resources') { + include 'assets/advantageworkstest/works/flat-generator.json' + } +} + def releaseJar = renamer.classes(tasks.named('jar', Jar)) { map.from minecraft.dependency.toSrgFile archiveClassifier = null @@ -174,7 +185,7 @@ def registerWorksRunner = { String taskName, String profileName, boolean automat classpath = sourceSets.runner.runtimeClasspath mainClass = 'zone.moddev.mc.advantageworks.runner.WorksRunner' if (!automated) standardInput = System.in - args '--profile', file("profiles/native118/${profileName}.json").absolutePath + args '--profile', file("profiles/native119/${profileName}.json").absolutePath if (automated) args '--automated' if (project.hasProperty('worksStations')) { args '--stations', project.property('worksStations').toString() @@ -189,7 +200,7 @@ def registerWorksRunner = { String taskName, String profileName, boolean automat } registerWorksRunner('runWorksDev', 'dev-core', false, - 'Builds and launches a disposable interactive 1.18.2 Advantage Works development server.') + 'Builds and launches a disposable interactive 1.19.4 Advantage Works development server.') registerWorksRunner('verifyWorksDev', 'dev-core', true, 'Generates, checks, restarts, and rechecks the disposable development works.') registerWorksRunner('verifyWorksTechProgression', 'dev-core-tech-progression', true, @@ -213,13 +224,13 @@ registerWorksRunner('verifyWorksWorldgenLegacyImport', 'worldgen-legacy-import', registerWorksRunner('runWorksPackagedCurrent', 'packaged-current', false, 'Launches the complete packaged works on localhost for manual inspection.') -tasks.register('verifyWorksNative118Inputs', JavaExec) { +tasks.register('verifyWorksNative119Inputs', JavaExec) { group = 'verification' - description = 'Checks 1.18.2 candidate identities and hashes without creating or opening a world.' + description = 'Checks 1.19.4 candidate identities and hashes without creating or opening a world.' dependsOn tasks.named('runnerClasses'), tasks.named('jar'), releaseJar classpath = sourceSets.runner.runtimeClasspath mainClass = 'zone.moddev.mc.advantageworks.runner.WorksRunner' - args '--profile', file("profiles/native118/${project.findProperty('worksProfile') ?: 'packaged-current'}.json").absolutePath, + args '--profile', file("profiles/native119/${project.findProperty('worksProfile') ?: 'packaged-current'}.json").absolutePath, '--validate-only' } @@ -228,7 +239,7 @@ tasks.register('verifyHarnessIsolation') { description = 'Confirms the PowerAdvantage release contains no test-harness content.' doLast { File powerJar = file(project.findProperty('powerAdvantageReleaseJar') - ?: '../build/libs/basePower-2.4.10.118021.jar') + ?: '../build/libs/basePower-2.4.11.119041.jar') if (!powerJar.isFile()) { throw new GradleException("Build PowerAdvantage first: ${powerJar}") } diff --git a/test-rig/client-resources/README.md b/test-rig/client-resources/README.md index e6be7673..df38e2ad 100644 --- a/test-rig/client-resources/README.md +++ b/test-rig/client-resources/README.md @@ -1,26 +1,26 @@ # Native Client Resource Check -This disposable Minecraft 1.18.2 / Forge 40.3.0 client checks models, textures, +This disposable Minecraft 1.19.4 / Forge 45.4.0 client checks models, textures, translations, recipe-book cells and machine screens after Minecraft loads them. It registers no gameplay content, opens no world and closes when finished. The probe is excluded from release jars and ordinary Eclipse launches. -See [port status](../../docs/1.18/PORT-STATUS.md) for current qualification. +See [port status](../../docs/1.19/PORT-STATUS.md) for current qualification. Earlier-version results do not qualify this version. Use Java 17 for Gradle, compilation and the client and server runners. The -target is official 1.18.2 mappings with MCP `20220404.173914`. +target is official 1.19.4 mappings with MCP `20230314.122934`. ## Build And Run ```powershell ./gradlew.bat check clientResourceProbeJar verifyEclipseProductionClasspath prepareCombinedEclipseLaunch ./test-rig/client-resources/test-launcher.ps1 -./test-rig/client-resources/run.ps1 -JavaHome '' -LaunchProfile runClientAllAdvantage.launch -RequireExtensions -MachineGuiLayouts -ChildRendererGeometry -RecipeBookFixtures '/unlock' -PreflightOnly -./test-rig/client-resources/run.ps1 -JavaHome '' -LaunchProfile runClientAllAdvantage.launch -RequireExtensions -MachineGuiLayouts -ChildRendererGeometry -RecipeBookFixtures '/unlock' +./test-rig/client-resources/run.ps1 -JavaHome '' -LaunchProfile runClientAllAdvantage.launch -RequireExtensions -MachineGuiLayouts -ChildRendererGeometry -RecipeBookFixtures '/unlock' -PreflightOnly +./test-rig/client-resources/run.ps1 -JavaHome '' -LaunchProfile runClientAllAdvantage.launch -RequireExtensions -MachineGuiLayouts -ChildRendererGeometry -RecipeBookFixtures '/unlock' ``` -The combined launch requires Power `2.4.10.118021`, Steam `2.2.11.118021` and -Electric `2.2.11.118021`. Pin the exact rebuilt jars in `profiles/native118` +The combined launch requires Power `2.4.11.119041`, Steam `2.2.12.119041` and +Electric `2.2.12.119041`. Pin the exact rebuilt jars in `profiles/native119` before regenerating it. Omitting the combined profile retains Power-only scope. Normal Eclipse launches contain production code only. @@ -39,10 +39,10 @@ is provenance, not proof that the selected loose output equals that jar. ## Recipe Fixtures -Generate fresh exports from the packaged 1.18.2 candidates, not a historical run: +Generate fresh exports from the packaged 1.19.4 candidates, not a historical run: ```powershell - -B test-rig/migration/native118_recipes.py --profile test-rig/profiles/native118/packaged-current.json --seed '' --inventory '' --inventory-sha256 '' --java17 '/bin/java.exe' --port 25577 --output test-rig/build/native118-recipes-new + -B test-rig/migration/native119_recipes.py --profile test-rig/profiles/native119/packaged-current.json --seed '' --inventory '' --inventory-sha256 '' --java17 '/bin/java.exe' --port 25577 --output test-rig/build/native119-recipes-new ``` Use an existing Python 3.11+ environment with `migration/requirements.txt`. diff --git a/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/ChildRendererGeometryProbe.java b/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/ChildRendererGeometryProbe.java index 2e735683..972310d8 100644 --- a/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/ChildRendererGeometryProbe.java +++ b/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/ChildRendererGeometryProbe.java @@ -63,7 +63,7 @@ static void verify(Minecraft minecraft, JsonObject result) { drill(minecraft, cases); turret(minecraft, cases); run(cases, "hydroturbine-registration", row -> { - EntityType type = ForgeRegistries.ENTITIES.getValue(new ResourceLocation("baseelectric:hydroturbine")); + EntityType type = ForgeRegistries.ENTITY_TYPES.getValue(new ResourceLocation("baseelectric:hydroturbine")); require(type != null, "Missing registered hydroturbine type"); EntityRenderer renderer = minecraft.getEntityRenderDispatcher().renderers.get(type); require(renderer != null && TURBINE_RENDERER.equals(renderer.getClass().getName()), "Missing production hydroturbine renderer"); @@ -175,7 +175,7 @@ && close(min(vertices, 1), 0.5F) && close(max(vertices, 1), 1F) } private static BlockEntity tile(String id) { - BlockEntityType type = ForgeRegistries.BLOCK_ENTITIES.getValue(new ResourceLocation(id)); + BlockEntityType type = ForgeRegistries.BLOCK_ENTITY_TYPES.getValue(new ResourceLocation(id)); require(type != null, "Missing registered tile type " + id); net.minecraft.world.level.block.state.BlockState state = ForgeRegistries.BLOCKS.getValues().stream() .map(net.minecraft.world.level.block.Block::defaultBlockState).filter(type::isValid).findFirst() @@ -193,8 +193,8 @@ private static List render(Minecraft minecraft, BlockEntity tile, Strin row.addProperty("partial_tick", partial); PoseStack matrices = new PoseStack(); PoseStack.Pose original = matrices.last(); - com.mojang.math.Matrix4f position = original.pose().copy(); - com.mojang.math.Matrix3f normal = original.normal().copy(); + org.joml.Matrix4f position = new org.joml.Matrix4f(original.pose()); + org.joml.Matrix3f normal = new org.joml.Matrix3f(original.normal()); Capture buffers = new Capture(minecraft, new ResourceLocation(expectedTexture), row); boolean balanced; Throwable renderFailure = null; @@ -249,18 +249,17 @@ private static final class Capture implements MultiBufferSource { void finish() throws Exception { for (Map.Entry entry : buffers.entrySet()) { BufferBuilder buffer = entry.getValue(); - buffer.end(); - Pair built = buffer.popNextBuffer(); - int count = built.getFirst().vertexCount(); + BufferBuilder.RenderedBuffer built = buffer.end(); + int count = built.drawState().vertexCount(); JsonObject batch = new JsonObject(); batches.add(batch); batch.addProperty("vertex_count", count); - batch.addProperty("format", built.getFirst().format().toString()); + batch.addProperty("format", built.drawState().format().toString()); JsonArray raw = new JsonArray(); batch.add("vertices_xyz_uv_normal_alpha", raw); require(count <= 4096, "Renderer exceeded the bounded geometry fixture"); - VertexFormat format = built.getFirst().format(); - ByteBuffer data = built.getSecond(); + VertexFormat format = built.drawState().format(); + ByteBuffer data = built.vertexBuffer(); int p = offset(format, VertexFormatElement.Usage.POSITION, 0); int uv = offset(format, VertexFormatElement.Usage.UV, 0); int n = offset(format, VertexFormatElement.Usage.NORMAL, 0); @@ -286,6 +285,7 @@ void finish() throws Exception { batch.addProperty("texture", selectedTexture.toString()); require(expectedTexture.equals(selectedTexture), "Production renderer selected the wrong texture"); texture(minecraft, selectedTexture, batch); + built.release(); } } } @@ -322,8 +322,8 @@ private static void beam(List vertices, Direction facing, float[] origi } private static void texture(Minecraft minecraft, ResourceLocation location, JsonObject row) throws IOException { - try (Resource resource = minecraft.getResourceManager().getResource(location)) { - BufferedImage image = ImageIO.read(resource.getInputStream()); + try (var resource = minecraft.getResourceManager().open(location)) { + BufferedImage image = ImageIO.read(resource); require(image != null && image.getWidth() > 0 && image.getHeight() > 0, "Renderer texture is not a readable image: " + location); row.addProperty("texture", location.toString()); row.addProperty("texture_width", image.getWidth()); diff --git a/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/ClientResourceProbe.java b/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/ClientResourceProbe.java index 74813bc7..ee0d7c91 100644 --- a/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/ClientResourceProbe.java +++ b/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/ClientResourceProbe.java @@ -10,7 +10,9 @@ import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.core.Direction; -import net.minecraft.core.NonNullList; +import net.minecraft.core.RegistryAccess; +import net.minecraft.world.flag.FeatureFlags; +import net.minecraft.world.item.CreativeModeTab; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.fml.common.Mod; @@ -26,7 +28,7 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; -import java.util.Random; +import net.minecraft.util.RandomSource; /** Runs after native resource baking, without opening or generating a world. */ @Mod(ClientResourceProbe.MODID) @@ -45,8 +47,8 @@ public final class ClientResourceProbe { try { result.addProperty("minecraft", net.minecraftforge.versions.mcp.MCPVersion.getMCVersion()); result.addProperty("forge", net.minecraftforge.versions.forge.ForgeVersion.getVersion()); - require("1.18.2".equals(result.get("minecraft").getAsString()) - && "40.3.0".equals(result.get("forge").getAsString()), "Wrong native client target"); + require("1.19.4".equals(result.get("minecraft").getAsString()) + && "45.4.0".equals(result.get("forge").getAsString()), "Wrong native client target"); require(minecraft.level == null && minecraft.player == null && !minecraft.hasSingleplayerServer(), "Resource probe must not open a world or connect to a server"); verifyNetworkBootstrap(result); @@ -65,27 +67,32 @@ public final class ClientResourceProbe { result.add("mod_versions", versions); require(versions.has("basepower") && zone.moddev.mc.basepower.PowerAdvantage.VERSION .equals(versions.get("basepower").getAsString()), "Power metadata does not match the compiled version"); - require("2.4.10.118021".equals(versions.get("basepower").getAsString()), "Wrong native Power candidate"); + require("2.4.11.119041".equals(versions.get("basepower").getAsString()), "Wrong native Power candidate"); for (String id : new String[]{"basesteam", "baseelectric"}) { - if (versions.has(id)) require("2.2.11.118021".equals(versions.get(id).getAsString()), "Wrong native expansion candidate: " + id); + if (versions.has(id)) require("2.2.12.119041".equals(versions.get(id).getAsString()), "Wrong native expansion candidate: " + id); } require(minecraft.getModelManager().getMissingModel() != null, "Timed out before model baking"); require(minecraft.getOverlay() == null, "Timed out before native resource loading completed"); int items = 0; int states = 0; + var expectedCreative = new java.util.HashSet(); + var hidden = java.util.Set.of("basepower:fluid_pipe_terminal", "basepower:crude_oil", "basepower:refined_oil", + "basesteam:pump_pipe_steam", "basesteam:drillbit", + "baseelectric:pump_pipe_electric", "baseelectric:laser_turret_evil"); BakedModel missing = minecraft.getModelManager().getMissingModel(); for (Item item : ForgeRegistries.ITEMS) { - ResourceLocation id = item.getRegistryName(); + ResourceLocation id = ForgeRegistries.ITEMS.getKey(item); if (id == null || !included(id.getNamespace())) continue; ItemStack stack = new ItemStack(item); String key = item.getDescriptionId(stack); require(!I18n.get(key).equals(key), "Untranslated item: " + id + " -> " + key); BakedModel model = minecraft.getItemRenderer().getModel(stack, null, null, 0); verifyModel(model, missing, null, id.toString(), true); + if (!hidden.contains(id.toString())) expectedCreative.add(id); items++; } for (Block block : ForgeRegistries.BLOCKS) { - ResourceLocation id = block.getRegistryName(); + ResourceLocation id = ForgeRegistries.BLOCKS.getKey(block); if (id == null || !included(id.getNamespace())) continue; for (BlockState state : block.getStateDefinition().getPossibleStates()) { BakedModel model = minecraft.getModelManager().getBlockModelShaper().getBlockModel(state); @@ -93,11 +100,27 @@ public final class ClientResourceProbe { states++; } } - NonNullList contents = NonNullList.create(); - ItemGroups.tab_powerAdvantage.fillItemList(contents); + CreativeModeTab tab = ItemGroups.tab_powerAdvantage; + require(tab != null, "The shared creative tab was not registered"); + tab.buildContents(new CreativeModeTab.ItemDisplayParameters(FeatureFlags.DEFAULT_FLAGS, false, + RegistryAccess.EMPTY)); + var contents = tab.getDisplayItems(); require(items > 0 && states > 0, "Power Advantage content is not registered"); require(!contents.isEmpty(), "Power Advantage creative tab is empty"); - require(!ItemGroups.tab_powerAdvantage.getIconItem().isEmpty(), "Power Advantage creative tab has no icon"); + require(!tab.getIconItem().isEmpty(), "Power Advantage creative tab has no icon"); + require(I18n.exists("itemGroup.basepower") && tab.getDisplayName().getString().equals(I18n.get("itemGroup.basepower")), + "The shared creative tab title is not translated"); + var creativeIds = new java.util.HashSet(); + for (ItemStack stack : contents) { + ResourceLocation id = ForgeRegistries.ITEMS.getKey(stack.getItem()); + require(id != null && creativeIds.add(id), "Duplicate or unregistered creative entry: " + id); + } + var missingCreative = new java.util.HashSet<>(expectedCreative); + missingCreative.removeAll(creativeIds); + var extraCreative = new java.util.HashSet<>(creativeIds); + extraCreative.removeAll(expectedCreative); + require(missingCreative.isEmpty() && extraCreative.isEmpty(), + "Wrong creative tab contents. Missing: " + missingCreative + "; unexpected: " + extraCreative); require(I18n.get("block.basepower.still").equals(I18n.get("tile.basepower.still.name")), "Legacy GUI translation differs from modern block name"); TankVisualProbe.verify(minecraft, result); @@ -142,12 +165,13 @@ private static void verifyNetworkBootstrap(JsonObject result) { private static void verifyModel(BakedModel model, BakedModel missing, BlockState state, String id, boolean requireQuads) { require(model != missing, "Missing baked model: " + id); - require(!"missingno".equals(model.getParticleIcon(net.minecraftforge.client.model.data.EmptyModelData.INSTANCE).getName().getPath()), "Missing particle texture: " + id); - Random random = new Random(42L); + require(!"missingno".equals(model.getParticleIcon(net.minecraftforge.client.model.data.ModelData.EMPTY) + .contents().name().getPath()), "Missing particle texture: " + id); + RandomSource random = RandomSource.create(42L); List quads = new ArrayList<>(model.getQuads(state, null, random)); for (Direction side : Direction.values()) quads.addAll(model.getQuads(state, side, random)); require(!requireQuads || !quads.isEmpty(), "Empty baked model: " + id); - for (BakedQuad quad : quads) require(!"missingno".equals(quad.getSprite().getName().getPath()), "Missing face texture: " + id); + for (BakedQuad quad : quads) require(!"missingno".equals(quad.getSprite().contents().name().getPath()), "Missing face texture: " + id); } private static void require(boolean condition, String message) { diff --git a/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/MachineGuiLayoutProbe.java b/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/MachineGuiLayoutProbe.java index 84ebff65..921ad827 100644 --- a/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/MachineGuiLayoutProbe.java +++ b/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/MachineGuiLayoutProbe.java @@ -34,7 +34,6 @@ import net.minecraft.world.inventory.Slot; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.TranslatableComponent; import net.minecraftforge.registries.ForgeRegistries; import zone.moddev.mc.basepower.registry.MachineGUIRegistry; @@ -78,10 +77,10 @@ static void verify(Minecraft minecraft, JsonObject result) { ForgeRegistries.BLOCKS.forEach(block -> { if (block instanceof GUIBlock && ((GUIBlock) block).getGuiID() > 0) blocks.add(block); }); - blocks.sort(Comparator.comparing(block -> block.getRegistryName().toString())); + blocks.sort(Comparator.comparing(block -> ForgeRegistries.BLOCKS.getKey(block).toString())); for (Block block : blocks) { JsonObject row = new JsonObject(); - String id = block.getRegistryName().toString(); + String id = ForgeRegistries.BLOCKS.getKey(block).toString(); row.addProperty("block", id); screens.add(row); try { @@ -90,8 +89,8 @@ static void verify(Minecraft minecraft, JsonObject result) { Field textureField = SimpleMachineGUI.class.getDeclaredField("guiDisplayImage"); textureField.setAccessible(true); net.minecraft.resources.ResourceLocation texture = (net.minecraft.resources.ResourceLocation) textureField.get(definition); - try (net.minecraft.server.packs.resources.Resource resource = minecraft.getResourceManager().getResource(texture)) { - BufferedImage artwork = ImageIO.read(resource.getInputStream()); + try (var resource = minecraft.getResourceManager().open(texture)) { + BufferedImage artwork = ImageIO.read(resource); // The GUI UVs use a logical 256-square atlas; Electric supplies 2x artwork. require(artwork != null && artwork.getWidth() >= 256 && artwork.getWidth() % 256 == 0 && artwork.getWidth() == artwork.getHeight(), @@ -107,6 +106,7 @@ static void verify(Minecraft minecraft, JsonObject result) { Field clientSide = net.minecraft.world.level.Level.class.getDeclaredField("isClientSide"); clientSide.setAccessible(true); clientSide.setBoolean(world, true); + player.level = world; tile.setLevel(world); SimpleMachineGUI.Container menu = ((SimpleMachineGUI) definition).createMenu(1, inventory, (Container) tile); require(expectations.getAsJsonObject("machines").has(id), "No human-facing label expectation for " + id); @@ -335,7 +335,7 @@ private static void verifyLongName(Minecraft minecraft, SimpleMachineGUI definit Inventory inventory) throws Exception { String name = String.join(" / ", java.util.Collections.nCopies(16, "John's oil reserve")); RecordingScreen screen = new RecordingScreen(definition, menu, inventory, - new net.minecraft.network.chat.TextComponent(name), name, "Inventory"); + net.minecraft.network.chat.Component.literal(name), name, "Inventory"); screen.init(minecraft, 320, 240); BufferedImage actual = render(minecraft, screen, VIEWPORTS[0], false); BufferedImage hidden = render(minecraft, screen, VIEWPORTS[0], true); @@ -379,6 +379,9 @@ private GuiWorld() { throw new AssertionError("Allocate the read-only GUI fixture without a world constructor"); } @Override public net.minecraft.world.level.block.state.BlockState getBlockState(BlockPos pos) { return state; } + @Override public net.minecraft.world.flag.FeatureFlagSet enabledFeatures() { + return net.minecraft.world.flag.FeatureFlags.DEFAULT_FLAGS; + } } private static final class RecordingScreen extends SimpleMachineGUI.GUIContainer { diff --git a/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/NativeGuiProjection.java b/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/NativeGuiProjection.java index 1e7d354d..3e02b863 100644 --- a/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/NativeGuiProjection.java +++ b/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/NativeGuiProjection.java @@ -2,16 +2,15 @@ import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.math.Matrix4f; +import org.joml.Matrix4f; /** Scoped native shader projection for framebuffer-based screen tests. */ final class NativeGuiProjection implements AutoCloseable { - private final Matrix4f projection = RenderSystem.getProjectionMatrix().copy(); + private final Matrix4f projection = new Matrix4f(RenderSystem.getProjectionMatrix()); private final PoseStack modelView = RenderSystem.getModelViewStack(); NativeGuiProjection(float width, float height, float near, float far, float depth) { - // Native Matrix4f takes top/bottom, unlike the old OpenGL bottom/top call. - RenderSystem.setProjectionMatrix(Matrix4f.orthographic(0, width, 0, height, near, far)); + RenderSystem.setProjectionMatrix(new Matrix4f().setOrtho(0, width, height, 0, near, far)); modelView.pushPose(); modelView.setIdentity(); modelView.translate(0, 0, depth); diff --git a/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/OilRenderProbe.java b/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/OilRenderProbe.java index 9bca3f7d..ce741f28 100644 --- a/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/OilRenderProbe.java +++ b/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/OilRenderProbe.java @@ -18,7 +18,8 @@ import net.minecraft.core.Direction; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; -import net.minecraft.core.Registry; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraftforge.registries.ForgeRegistries; import net.minecraft.tags.FluidTags; import net.minecraft.tags.TagKey; import net.minecraft.world.level.BlockAndTintGetter; @@ -47,7 +48,7 @@ static void verify(Minecraft minecraft, JsonObject result) { net.minecraft.world.level.material.Fluids.FLOWING_WATER.builtInRegistryHolder())); synthetic.put(FluidTags.LAVA, List.of(net.minecraft.world.level.material.Fluids.LAVA.builtInRegistryHolder(), net.minecraft.world.level.material.Fluids.FLOWING_LAVA.builtInRegistryHolder())); - Registry.FLUID.bindTags(synthetic); + BuiltInRegistries.FLUID.bindTags(synthetic); try { requireBucket(Items.crude_oil_bucket, Fluids.crude_oil); requireBucket(Items.refined_oil_bucket, Fluids.refined_oil); @@ -64,15 +65,15 @@ static void verify(Minecraft minecraft, JsonObject result) { valid &= check(minecraft, net.minecraft.world.level.material.Fluids.LAVA.defaultFluidState(), "minecraft:block/lava_still", false, cases); if (!valid) throw new IllegalStateException("Native fluid renderer used the wrong sprite or tint: " + cases); } finally { - Registry.FLUID.resetTags(); - Registry.FLUID.bindTags(previous); + BuiltInRegistries.FLUID.resetTags(); + BuiltInRegistries.FLUID.bindTags(previous); require(fluidTags().equals(previous), "Synthetic fluid tag membership was not restored"); } } private static Map, List>> fluidTags() { Map, List>> tags = new HashMap<>(); - Registry.FLUID.getTags().forEach(tag -> { + BuiltInRegistries.FLUID.getTags().forEach(tag -> { List> values = tag.getSecond().stream().toList(); if (!values.isEmpty()) tags.put(tag.getFirst(), values); }); @@ -81,10 +82,10 @@ private static Map, List>> fluidTags() { private static void requireBucket(net.minecraft.world.item.Item bucket, Fluid expected) { net.minecraftforge.fluids.FluidStack contents = net.minecraftforge.fluids.FluidUtil.getFluidContained(new ItemStack(bucket)) - .orElseThrow(() -> new IllegalStateException("Bucket has no native fluid capability: " + bucket.getRegistryName())); + .orElseThrow(() -> new IllegalStateException("Bucket has no native fluid capability: " + ForgeRegistries.ITEMS.getKey(bucket))); if (!(bucket instanceof BucketItem) || ((BucketItem) bucket).getFluid() != expected || expected.getBucket() != bucket || contents.getFluid() != expected || contents.getAmount() != 1000) { - throw new IllegalStateException("Wrong bucket fluid: " + bucket.getRegistryName()); + throw new IllegalStateException("Wrong bucket fluid: " + ForgeRegistries.ITEMS.getKey(bucket)); } } @@ -100,37 +101,36 @@ private static boolean check(Minecraft minecraft, FluidState state, String textu @Override public int getBrightness(LightLayer type, BlockPos pos) { return 15; } @Override public LevelLightEngine getLightEngine() { throw new UnsupportedOperationException("Synthetic light is supplied by getLightFor"); } @Override public int getBlockTint(BlockPos pos, ColorResolver resolver) { - return resolver.getColor(net.minecraft.data.BuiltinRegistries.BIOME.get( - net.minecraft.world.level.biome.Biomes.PLAINS), pos.getX(), pos.getZ()); + return 0x3F76E4; // Vanilla plains water tint for this read-only witness. } }; BufferBuilder buffer = new BufferBuilder(4096); buffer.begin(com.mojang.blaze3d.vertex.VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK); - boolean rendered = minecraft.getBlockRenderer().renderLiquid( + minecraft.getBlockRenderer().renderLiquid( BlockPos.ZERO, world, buffer, world.getBlockState(BlockPos.ZERO), state); - buffer.end(); - com.mojang.datafixers.util.Pair built = buffer.popNextBuffer(); - int vertices = built.getFirst().vertexCount(); + BufferBuilder.RenderedBuffer built = buffer.end(); + int vertices = built.drawState().vertexCount(); TextureAtlasSprite sprite = minecraft.getTextureAtlas(TextureAtlas.LOCATION_BLOCKS).apply(new ResourceLocation(texture)); - require(rendered && vertices >= 4, "Native fluid renderer emitted no top face for " + state); - ByteBuffer data = built.getSecond(); + require(vertices >= 4, "Native fluid renderer emitted no top face for " + state); + ByteBuffer data = built.vertexBuffer(); int uvOffset = elementOffset(VertexFormatElement.Usage.UV, 0); float u = data.getFloat(uvOffset); float v = data.getFloat(uvOffset + 4); int colorOffset = elementOffset(VertexFormatElement.Usage.COLOR, 0); int rgb = (data.get(colorOffset) & 255) << 16 | (data.get(colorOffset + 1) & 255) << 8 | data.get(colorOffset + 2) & 255; - boolean correctSprite = !"missingno".equals(sprite.getName().getPath()) + boolean correctSprite = !"missingno".equals(sprite.contents().name().getPath()) && u >= sprite.getU0() && u <= sprite.getU1() && v >= sprite.getV0() && v <= sprite.getV1(); boolean correctTint = !(state.getType() instanceof Fluids.PowerFluid) || rgb == 0xFFFFFF; JsonObject entry = new JsonObject(); - entry.addProperty("fluid", state.getType().getRegistryName().toString()); - entry.addProperty("block", state.createLegacyBlock().getBlock().getRegistryName().toString()); + entry.addProperty("fluid", ForgeRegistries.FLUIDS.getKey(state.getType()).toString()); + entry.addProperty("block", ForgeRegistries.BLOCKS.getKey(state.createLegacyBlock().getBlock()).toString()); entry.addProperty("expected_sprite", texture); entry.addProperty("correct_sprite", correctSprite); entry.addProperty("vertex_rgb", String.format("%06X", rgb)); entry.addProperty("vertices", vertices); cases.add(entry); - return rendered && vertices > 0 && correctSprite && correctTint; + built.release(); + return correctSprite && correctTint; } private static int elementOffset(VertexFormatElement.Usage usage, int index) { diff --git a/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/RecipeBookGroupingProbe.java b/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/RecipeBookGroupingProbe.java index b5ebaac3..e9ff1ce9 100644 --- a/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/RecipeBookGroupingProbe.java +++ b/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/RecipeBookGroupingProbe.java @@ -54,15 +54,15 @@ static void verify(Minecraft minecraft, JsonObject report) { net.minecraft.nbt.CompoundTag saved; try (java.io.InputStream input = Files.newInputStream(root.resolve("server-registries.dat"))) { net.minecraft.nbt.CompoundTag level = net.minecraft.nbt.NbtIo.readCompressed(input); - require("1.18.2".equals(level.getCompound("Data").getCompound("Version").getString("Name")), - "Recipe fixture is not a native 1.18.2 server export"); + require("1.19.4".equals(level.getCompound("Data").getCompound("Version").getString("Name")), + "Recipe fixture is not a native 1.19.4 server export"); net.minecraft.nbt.ListTag mods = level.getCompound("fml").getList("LoadingModList", 10); boolean nativeForge = false; for (int i = 0; i < mods.size(); i++) { net.minecraft.nbt.CompoundTag mod = mods.getCompound(i); - if ("forge".equals(mod.getString("ModId"))) nativeForge = "40.3.0".equals(mod.getString("ModVersion")); + if ("forge".equals(mod.getString("ModId"))) nativeForge = "45.4.0".equals(mod.getString("ModVersion")); } - require(nativeForge, "Recipe fixture was not saved by Forge 40.3.0"); + require(nativeForge, "Recipe fixture was not saved by Forge 45.4.0"); saved = level.getCompound("fml").getCompound("Registries"); } String itemRegistry = net.minecraftforge.registries.ForgeRegistries.ITEMS.getRegistryName().toString(); @@ -92,11 +92,11 @@ static void verify(Minecraft minecraft, JsonObject report) { for (Recipe recipe : packet.getRecipes()) { JsonObject row = expected.get(recipe.getId().toString()); require(row != null, "Unexpected network recipe " + recipe.getId()); - String output = recipe.getResultItem().getItem().getRegistryName().toString(); - require(output.equals(row.get("output").getAsString()) && recipe.getResultItem().getCount() == row.get("count").getAsInt(), + String output = net.minecraftforge.registries.ForgeRegistries.ITEMS.getKey(recipe.getResultItem(net.minecraft.core.RegistryAccess.EMPTY).getItem()).toString(); + require(output.equals(row.get("output").getAsString()) && recipe.getResultItem(net.minecraft.core.RegistryAccess.EMPTY).getCount() == row.get("count").getAsInt(), "Client/server item registry mismatch for " + recipe.getId()); require(recipe.getGroup().equals(row.get("group").getAsString()) && recipe.getGroup().equals(output), "Group changed in recipe packet"); - String type = net.minecraft.core.Registry.RECIPE_TYPE.getKey(recipe.getType()).toString(); + String type = net.minecraft.core.registries.BuiltInRegistries.RECIPE_TYPE.getKey(recipe.getType()).toString(); require(type.equals(row.get("type").getAsString()), "Recipe type changed in packet"); cellKeys.add(type + "|" + output); } @@ -104,10 +104,10 @@ static void verify(Minecraft minecraft, JsonObject report) { require(manager.getRecipes().size() == expected.size(), "Missing network recipes"); ClientRecipeBook book = new ClientRecipeBook(); manager.getRecipes().forEach(book::add); - book.setupCollections(manager.getRecipes()); + book.setupCollections(manager.getRecipes(), net.minecraft.core.RegistryAccess.EMPTY); int counted = 0; for (RecipeCollection cell : book.getCollections()) { - Set outputs = cell.getRecipes().stream().map(recipe -> recipe.getResultItem().getItem().getRegistryName().toString()) + Set outputs = cell.getRecipes().stream().map(recipe -> net.minecraftforge.registries.ForgeRegistries.ITEMS.getKey(recipe.getResultItem(net.minecraft.core.RegistryAccess.EMPTY).getItem()).toString()) .collect(Collectors.toSet()); require(outputs.size() == 1 && cell.hasSingleResultItem(), "Mixed items share a native recipe-book cell: " + outputs); counted += cell.getRecipes().size(); @@ -134,10 +134,10 @@ private static void render(Minecraft minecraft, ClientRecipeBook book) throws Ex "baseelectric:integrated_circuit", "baseelectric:control_circuit", "baseelectric:psu", "basepower:item_conveyor")); for (RecipeCollection cell : book.getCollections()) { Recipe recipe = cell.getRecipes().get(0); - String id = recipe.getResultItem().getItem().getRegistryName().toString(); + String id = net.minecraftforge.registries.ForgeRegistries.ITEMS.getKey(recipe.getResultItem(net.minecraft.core.RegistryAccess.EMPTY).getItem()).toString(); if (selected.contains(id) || id.startsWith("basepower:item_filter_")) shown.add(cell); } - shown.sort(java.util.Comparator.comparing(cell -> cell.getRecipes().get(0).getResultItem().getItem().getRegistryName().toString())); + shown.sort(java.util.Comparator.comparing(cell -> net.minecraftforge.registries.ForgeRegistries.ITEMS.getKey(cell.getRecipes().get(0).getResultItem(net.minecraft.core.RegistryAccess.EMPTY).getItem()).toString())); require(shown.size() == 13, "The thirteen reported circuit/conveyor cells must be separate"); int width = 1000, height = 180; RenderTarget target = new com.mojang.blaze3d.pipeline.TextureTarget(width, height, true, Minecraft.ON_OSX); @@ -164,7 +164,7 @@ private static void render(Minecraft minecraft, ClientRecipeBook book) throws Ex } button.setPosition(x, y); button.render(new com.mojang.blaze3d.vertex.PoseStack(), -1, -1, 1F); - minecraft.font.draw(new com.mojang.blaze3d.vertex.PoseStack(), shown.get(i).getRecipes().get(0).getResultItem().getDisplayName().getString(), x + 30, y + 8, 0xFFFFFF); + minecraft.font.draw(new com.mojang.blaze3d.vertex.PoseStack(), shown.get(i).getRecipes().get(0).getResultItem(net.minecraft.core.RegistryAccess.EMPTY).getDisplayName().getString(), x + 30, y + 8, 0xFFFFFF); } ByteBuffer pixels = ByteBuffer.allocateDirect(width * height * 4); RenderSystem.readPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); diff --git a/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/TankVisualProbe.java b/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/TankVisualProbe.java index 2a5d5b77..c4678ea0 100644 --- a/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/TankVisualProbe.java +++ b/test-rig/client-resources/java/zone/moddev/mc/advantageworks/client/TankVisualProbe.java @@ -88,7 +88,7 @@ private static int renderWater(Minecraft minecraft, MetalTankTileEntity tile) { SimpleMachineGUI.Container container = definition.createMenu(1, inventory, tile); require(container.getType() == MachineGUIRegistry.TYPE && MachineGUIRegistry.TYPE != null, "Tank GUI is not backed by the registered native container type"); - SimpleMachineGUI.GUIContainer screen = container.createScreen(inventory, new net.minecraft.network.chat.TextComponent("Tank probe")); + SimpleMachineGUI.GUIContainer screen = container.createScreen(inventory, net.minecraft.network.chat.Component.literal("Tank probe")); screen.init(minecraft, width, height); require(screen.getMinecraft() == minecraft, "Native tank screen was not initialized"); tile.getTank().setFluid(new cyano.poweradvantage.api.fluid.FluidStack(net.minecraft.world.level.material.Fluids.WATER, 10000)); @@ -98,7 +98,6 @@ private static int renderWater(Minecraft minecraft, MetalTankTileEntity tile) { RenderSystem.disableDepthTest(); RenderSystem.enableBlend(); RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); - RenderSystem.enableTexture(); RenderSystem.setShaderColor(1F, 1F, 1F, 1F); try { RenderSystem.clearColor(0F, 0F, 0F, 1F); diff --git a/test-rig/client-resources/resources/META-INF/mods.toml b/test-rig/client-resources/resources/META-INF/mods.toml index d17d3f5e..b1fc7ffc 100644 --- a/test-rig/client-resources/resources/META-INF/mods.toml +++ b/test-rig/client-resources/resources/META-INF/mods.toml @@ -1,5 +1,5 @@ modLoader="javafml" -loaderVersion="[40,41)" +loaderVersion="[45,46)" license="GPL-3.0-only" [[mods]] @@ -11,20 +11,20 @@ description='''Build-only model, texture and translation checks. Never distribut [[dependencies.advantageclientresources]] modId="basepower" mandatory=true -versionRange="[2.4.10.118021]" +versionRange="[2.4.11.119041]" ordering="AFTER" side="CLIENT" [[dependencies.advantageclientresources]] modId="forge" mandatory=true -versionRange="[40.3.0]" +versionRange="[45.4.0]" ordering="NONE" side="CLIENT" [[dependencies.advantageclientresources]] modId="minecraft" mandatory=true -versionRange="[1.18.2]" +versionRange="[1.19.4]" ordering="NONE" side="CLIENT" diff --git a/test-rig/client-resources/run.ps1 b/test-rig/client-resources/run.ps1 index f5e4115a..d866969b 100644 --- a/test-rig/client-resources/run.ps1 +++ b/test-rig/client-resources/run.ps1 @@ -30,19 +30,19 @@ function Assert-NativeMetadata([string]$Metadata, [string]$Id) { # Match the known single-mod header, not dependency modId/versionRange fields. $header = [regex]::Match($Metadata, '(?ms)^\[\[mods\]\]\s*(.*?)(?=^\[|\z)').Groups[1].Value if ($header -notmatch ('(?m)^modId\s*=\s*"' + [regex]::Escape($Id) + '"[ \t]*(?:#[^\r\n]*)?\r?$') -or - $header -notmatch '(?m)^version\s*=\s*"[^"\r\n]*\.118021"[ \t]*(?:#[^\r\n]*)?\r?$' -or - $Metadata -notmatch '(?m)^loaderVersion\s*=\s*"\[40,') { throw "Not a native Forge 40 candidate: $Id" } - $expected = @{ basepower = '2.4.10.118021'; basesteam = '2.2.11.118021'; baseelectric = '2.2.11.118021' } + $header -notmatch '(?m)^version\s*=\s*"[^"\r\n]*\.119041"[ \t]*(?:#[^\r\n]*)?\r?$' -or + $Metadata -notmatch '(?m)^loaderVersion\s*=\s*"\[45,') { throw "Not a native Forge 45 candidate: $Id" } + $expected = @{ basepower = '2.4.11.119041'; basesteam = '2.2.12.119041'; baseelectric = '2.2.12.119041' } if ($expected.ContainsKey($Id) -and $header -notmatch ('(?m)^version\s*=\s*"' + [regex]::Escape($expected[$Id]) + '"[ \t]*(?:#[^\r\n]*)?\r?$')) { throw "Unexpected native candidate version for $Id; expected $($expected[$Id])" } } function Assert-NativeLaunch([hashtable]$Environment, [hashtable]$Attributes) { - if ($Environment.MC_VERSION -ne '1.18.2' -or $Environment.FORGE_VERSION -ne '40.3.0' -or - $Environment.MCP_MAPPINGS -ne 'official_1.18.2' -or $Environment.MCP_VERSION -ne '20220404.173914' -or - $Environment.target -ne 'fmluserdevclient') { throw 'Regenerate the native 1.18.2 / Forge 40.3.0 official client launch.' } + if ($Environment.MC_VERSION -ne '1.19.4' -or $Environment.FORGE_VERSION -ne '45.4.0' -or + $Environment.MCP_MAPPINGS -ne 'official_1.19.4' -or $Environment.MCP_VERSION -ne '20230314.122934' -or + $Environment.target -ne 'fmluserdevclient') { throw 'Regenerate the native 1.19.4 / Forge 45.4.0 official client launch.' } $program = $Attributes['org.eclipse.jdt.launching.PROGRAM_ARGUMENTS'] - if ($program -notmatch '1\.18\.2-40\.3\.0' -or + if ($program -notmatch '1\.19\.4-45\.4\.0' -or $program -notmatch '--main cpw\.mods\.bootstraplauncher\.BootstrapLauncher\b' -or $program -notmatch '--launchTarget forgeclientuserdev\b' -or $program -match '1\.(?:13\.2|14\.4|15\.2|16\.5|17\.1)|--(?:server|port|world)\b' -or @@ -53,14 +53,14 @@ function Assert-NativeLaunch([hashtable]$Environment, [hashtable]$Attributes) { } } function Assert-NativeArchive([string]$Path) { - if ($Path -match '(?:forge|client-extra)-1\.(?:13\.2|14\.4|15\.2|16\.5|17\.1)|[\\/]PA11[34567][\\/]') { throw "Legacy runtime archive: $Path" } + if ($Path -match '(?:forge|client-extra)-1\.(?:13\.2|14\.4|15\.2|16\.5|17\.1)|[\\/]PA11[345678][\\/]') { throw "Legacy runtime archive: $Path" } } function Quote-Argument([string]$Value) { '"' + [regex]::Replace([regex]::Replace($Value, '(\\*)"', '$1$1\"'), '(\\+)$', '$1$1') + '"' } $probe = Join-Path $project 'build/client-resource-probe/advantageclientresources-deobf.jar' $runtime = Join-Path $project ('run-client-resource-runs/' + [DateTime]::UtcNow.ToString('yyyyMMdd-HHmmss') + '-' + [guid]::NewGuid().ToString('N').Substring(0, 8)) -$inputReport = [ordered]@{ minecraft = '1.18.2'; forge = '40.3.0'; mappings = 'official_1.18.2'; mcp = '20220404.173914'; qualification = 'UNQUALIFIED'; output_source = $OutputSource; launch_profile = $LaunchProfile; require_extensions = [bool]$RequireExtensions; child_renderer_geometry = [bool]$ChildRendererGeometry } +$inputReport = [ordered]@{ minecraft = '1.19.4'; forge = '45.4.0'; mappings = 'official_1.19.4'; mcp = '20230314.122934'; qualification = 'UNQUALIFIED'; output_source = $OutputSource; launch_profile = $LaunchProfile; require_extensions = [bool]$RequireExtensions; child_renderer_geometry = [bool]$ChildRendererGeometry } $inputReport.machine_gui_layouts = [bool]$MachineGuiLayouts $phase = 'launch-preflight' New-Item -ItemType Directory -Path $runtime | Out-Null @@ -76,8 +76,8 @@ $inputReport.java = Get-Record $java $inputReport.launch = Get-Record (Join-Path $project $LaunchProfile) $inputReport.probe = Get-Record $probe $probeMetadata = Read-JarMetadata $probe -if ($probeMetadata -notmatch 'loaderVersion="\[40,41\)"' -or $probeMetadata -notmatch 'versionRange="\[1\.18\.2\]"' -or - $probeMetadata -notmatch 'versionRange="\[40\.3\.0\]"' -or $probeMetadata -notmatch 'versionRange="\[2\.4\.10\.118021\]"') { throw 'Rebuild the native 1.18 probe; do not substitute a legacy probe.' } +if ($probeMetadata -notmatch 'loaderVersion="\[45,46\)"' -or $probeMetadata -notmatch 'versionRange="\[1\.19\.4\]"' -or + $probeMetadata -notmatch 'versionRange="\[45\.4\.0\]"' -or $probeMetadata -notmatch 'versionRange="\[2\.4\.11\.119041\]"') { throw 'Rebuild the native 1.19 probe; do not substitute a legacy probe.' } Copy-Item -LiteralPath (Join-Path $project $LaunchProfile) -Destination (Join-Path $runtime 'launch.xml') $environment = @{} foreach ($map in $launch.launchConfiguration.mapAttribute) { @@ -108,7 +108,7 @@ $outputFiles = @(Get-ChildItem -LiteralPath $modOutput -Recurse -File | Sort-Obj if (@($outputFiles | Where-Object { $_.path -like '*.class' }).Count -eq 0) { throw 'Selected output has no compiled production classes.' } $outputFiles | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath (Join-Path $runtime 'production-files.json') -Encoding utf8 $inputReport.production_files = Get-Record (Join-Path $runtime 'production-files.json') -if (-not $PowerCandidate) { $PowerCandidate = Join-Path $project 'build/libs-dev/basePower-2.4.10.118021-deobf.jar' } +if (-not $PowerCandidate) { $PowerCandidate = Join-Path $project 'build/libs-dev/basePower-2.4.11.119041-deobf.jar' } $PowerCandidate = (Resolve-Path -LiteralPath $PowerCandidate).Path Assert-NativeMetadata (Read-JarMetadata $PowerCandidate) 'basepower' $inputReport.power_candidate = Get-Record $PowerCandidate @@ -174,7 +174,7 @@ foreach ($archive in $archives) { $archives = $launchArchives $inputReport.archives = $archiveRecords $inputReport.runtime_candidates = $runtimeCandidates -if (-not ($archives -match 'forge-1\.18\.2-40\.3\.0\.jar$')) { throw 'Missing actual native mapped Forge runtime.' } +if (-not ($archives -match 'forge-1\.19\.4-45\.4\.0\.jar$')) { throw 'Missing actual native mapped Forge runtime.' } if ($RequireExtensions -and (-not $runtimeCandidates.ContainsKey('basesteam') -or -not $runtimeCandidates.ContainsKey('baseelectric'))) { throw 'Combined launch must explicitly contain both native expansion candidates.' } $arguments = @([regex]::Matches($attributes['org.eclipse.jdt.launching.PROGRAM_ARGUMENTS'], '"[^"]*"|\S+') | ForEach-Object { $_.Value.Trim('"') }) $vmArguments = @([regex]::Matches($attributes['org.eclipse.jdt.launching.VM_ARGUMENTS'], '"[^"]*"|\S+') | ForEach-Object { $_.Value.Trim('"') }) @@ -214,7 +214,7 @@ $resultPath = Join-Path $runtime 'client-resources.json' if (-not (Test-Path -LiteralPath $resultPath)) { throw "Client did not produce a result; inspect $runtime/logs" } $result = Get-Content -LiteralPath $resultPath -Raw | ConvertFrom-Json if ($result.status -ne 'PASS') { throw "Client resource check failed: $($result.error); inspect $runtime" } -if ($result.minecraft -ne '1.18.2' -or $result.forge -ne '40.3.0') { throw 'Result is not native 1.18.2 / Forge 40.3.0 evidence.' } +if ($result.minecraft -ne '1.19.4' -or $result.forge -ne '45.4.0') { throw 'Result is not native 1.19.4 / Forge 45.4.0 evidence.' } if ($MachineGuiLayouts -and $result.machine_gui_layout_status -ne 'PASS') { throw 'Machine GUI layouts did not pass; inspect the retained images and JSON.' } $expectedGuiTextChecks = if ($RequireExtensions) { 351 } else { 126 } if ($MachineGuiLayouts -and ($result.machine_gui_text_spec -ne 2 -or $result.machine_gui_text_status -ne 'PASS' -or diff --git a/test-rig/client-resources/test-launcher.ps1 b/test-rig/client-resources/test-launcher.ps1 index e5d9e3e3..70b6cf61 100644 --- a/test-rig/client-resources/test-launcher.ps1 +++ b/test-rig/client-resources/test-launcher.ps1 @@ -76,33 +76,33 @@ if ($CrossVersionEvidenceRoots) { $native = @' modLoader="javafml" -loaderVersion="[40,)" # native Forge +loaderVersion="[45,)" # native Forge [[mods]] # mandatory header modId="basepower" # inline comment -version="2.4.10.118021" # native candidate +version="2.4.11.119041" # native candidate [[dependencies.basepower]] modId="minecraft" mandatory=true -versionRange="[1.18.2]" +versionRange="[1.19.4]" '@ Assert-NativeMetadata $native 'basepower' $checks++ -Assert-Throws { Assert-NativeMetadata ($native.Replace('2.4.10.118021','2.4.4.113021')) 'basepower' } -Assert-Throws { Assert-NativeMetadata ($native.Replace('2.4.10.118021','2.4.5.114041')) 'basepower' } -Assert-Throws { Assert-NativeMetadata ($native.Replace('2.4.10.118021','2.4.5.115021')) 'basepower' } -Assert-Throws { Assert-NativeMetadata ($native.Replace('[40,)','[36,)')) 'basepower' } -Assert-Throws { Assert-NativeMetadata ($native.Replace('2.4.10.118021','2.4.7.116051')) 'basepower' } -Assert-Throws { Assert-NativeMetadata ($native.Replace('2.4.10.118021','${version}')) 'basepower' } +Assert-Throws { Assert-NativeMetadata ($native.Replace('2.4.11.119041','2.4.4.113021')) 'basepower' } +Assert-Throws { Assert-NativeMetadata ($native.Replace('2.4.11.119041','2.4.5.114041')) 'basepower' } +Assert-Throws { Assert-NativeMetadata ($native.Replace('2.4.11.119041','2.4.5.115021')) 'basepower' } +Assert-Throws { Assert-NativeMetadata ($native.Replace('[45,)','[36,)')) 'basepower' } +Assert-Throws { Assert-NativeMetadata ($native.Replace('2.4.11.119041','2.4.7.116051')) 'basepower' } +Assert-Throws { Assert-NativeMetadata ($native.Replace('2.4.11.119041','${version}')) 'basepower' } Assert-Throws { Assert-NativeMetadata $native 'baseelectric' } Assert-Throws { Assert-NativeMetadata '' 'basepower' } foreach ($id in 'basesteam','baseelectric') { - $expansion = $native.Replace('basepower', $id).Replace('2.4.10.118021','2.2.11.118021') + $expansion = $native.Replace('basepower', $id).Replace('2.4.11.119041','2.2.12.119041') Assert-NativeMetadata $expansion $id $checks++ - Assert-Throws { Assert-NativeMetadata ($expansion.Replace('2.2.11.118021','2.2.6.115021')) $id } + Assert-Throws { Assert-NativeMetadata ($expansion.Replace('2.2.12.119041','2.2.6.115021')) $id } } -$environment = @{ MC_VERSION = '1.18.2'; FORGE_VERSION = '40.3.0'; MCP_MAPPINGS = 'official_1.18.2'; MCP_VERSION = '20220404.173914'; target = 'fmluserdevclient' } -$attributes = @{ 'org.eclipse.jdt.launching.PROGRAM_ARGUMENTS' = '--main cpw.mods.bootstraplauncher.BootstrapLauncher --cache D:\native\1.18.2-40.3.0 -- --launchTarget forgeclientuserdev --gameDir .'; 'org.eclipse.jdt.launching.MAIN_TYPE' = 'net.minecraftforge.launcher.Main' } +$environment = @{ MC_VERSION = '1.19.4'; FORGE_VERSION = '45.4.0'; MCP_MAPPINGS = 'official_1.19.4'; MCP_VERSION = '20230314.122934'; target = 'fmluserdevclient' } +$attributes = @{ 'org.eclipse.jdt.launching.PROGRAM_ARGUMENTS' = '--main cpw.mods.bootstraplauncher.BootstrapLauncher --cache D:\native\1.19.4-45.4.0 -- --launchTarget forgeclientuserdev --gameDir .'; 'org.eclipse.jdt.launching.MAIN_TYPE' = 'net.minecraftforge.launcher.Main' } Assert-NativeLaunch $environment $attributes $checks++ foreach ($key in 'MC_VERSION','FORGE_VERSION','MCP_MAPPINGS','MCP_VERSION','target') { @@ -118,7 +118,7 @@ foreach ($argument in '--server localhost','--port 25565','--gameDir D:\protecte $invalid = $attributes.Clone() $invalid['org.eclipse.jdt.launching.MAIN_TYPE'] = 'net.minecraft.client.main.Main' Assert-Throws { Assert-NativeLaunch $environment $invalid } -Assert-NativeArchive 'D:\native\forge-1.18.2-40.3.0.jar' +Assert-NativeArchive 'D:\native\forge-1.19.4-45.4.0.jar' $checks++ foreach ($path in 'D:\PA114\client.jar','D:\PA113\client.jar','D:\PA116\client.jar','D:\native\forge-1.16.5-36.2.34.jar','D:\native\forge-1.14.4-28.2.26.jar','D:\native\client-extra-1.13.2.jar') { Assert-Throws { Assert-NativeArchive $path } @@ -135,7 +135,7 @@ if ($StaticOnly) { return } $project = (Resolve-Path (Join-Path $PSScriptRoot '../..')).Path -$power = Join-Path $project 'build/libs-dev/basePower-2.4.10.118021-deobf.jar' +$power = Join-Path $project 'build/libs-dev/basePower-2.4.11.119041-deobf.jar' Assert-NativeMetadata (Read-JarMetadata $power) 'basepower' $checks++ [xml]$launch = Get-Content -LiteralPath (Join-Path $project 'runClientAllAdvantage.launch') -Raw diff --git a/test-rig/gradle.properties b/test-rig/gradle.properties index e824be5c..f9273f60 100644 --- a/test-rig/gradle.properties +++ b/test-rig/gradle.properties @@ -1,12 +1,12 @@ org.gradle.jvmargs=-Xmx2G org.gradle.daemon=false -minecraft_version=1.18.2 -forge_version=40.3.0 +minecraft_version=1.19.4 +forge_version=45.4.0 mapping_channel=official -mapping_version=1.18.2 -mcp_version=20220404.173914 -harness_version=1.1.2.118021 -poweradvantage_deobf_sha256=C00F835A7A80BAF332355EF1F181F252B603A017A979D2F41EA3BBEAE314E280 -poweradvantage_source_commit=bab9971cc2d593eee1f4a3de6dafc7ec268d25c2 +mapping_version=1.19.4 +mcp_version=20230314.122934 +harness_version=1.1.3.119041 +poweradvantage_deobf_sha256=512FDEC9275B33533EF773C110E6229E0F07268775FAD4B5942A15B68A9EB87D +poweradvantage_source_commit=c9995315e2aee9d9bffec8eb8b1191ad8a0faeb7 poweradvantage_pin_status=SOURCE_AND_ARTIFACT_PINNED diff --git a/test-rig/migration/audit_native118_endurance.py b/test-rig/migration/audit_native119_endurance.py similarity index 92% rename from test-rig/migration/audit_native118_endurance.py rename to test-rig/migration/audit_native119_endurance.py index a81ef413..d283b453 100644 --- a/test-rig/migration/audit_native118_endurance.py +++ b/test-rig/migration/audit_native119_endurance.py @@ -1,4 +1,4 @@ -"""Replay every station checkpoint from a completed 1.18.2 endurance run.""" +"""Replay every station checkpoint from a completed 1.19.4 endurance run.""" import argparse from collections import Counter @@ -10,14 +10,14 @@ from matrix import digest, write_json from native117 import require_disjoint from native117_broad import read_json -import native118 +import native119 def replay(run, profile, specification): - report = read_json(run / "native118-endurance-results.json") - modes = ("fresh118", "retained-upgrade-native118") + report = read_json(run / "native119-endurance-results.json") + modes = ("fresh119", "retained-upgrade-native119") require = endurance.require - require(report["status"] == "PASS" and report["minecraft"] == native118.MINECRAFT + require(report["status"] == "PASS" and report["minecraft"] == native119.MINECRAFT and report["duration_seconds"] == 1800 and report["interval_seconds"] == 60 and report["retained_unchanged"] is True and set(report["profiles"]) == set(modes), "Expected a complete fresh and migrated 30-minute run") @@ -42,16 +42,16 @@ def replay(run, profile, specification): for mode in modes: runtime = run / mode station_path = runtime / "advantage-works-results" - native118.verify_runtime(runtime) + native119.verify_runtime(runtime) require({jar.name: digest(jar) for jar in (runtime / "mods").glob("*.jar")} == expected_jars, mode + ": staged artifacts differ from the approved profile") for boot in ("endurance", "endurance-restart"): - native118.require_native_boot(runtime / f"console-{boot}.log") + native119.require_native_boot(runtime / f"console-{boot}.log") rig = endurance.Rig(mode, runtime, None, None, definitions) def snapshot(station, label): document = read_json(station_path / f"{station}-checkpoint-{label}.json") - require(document["minecraft"] == native118.MINECRAFT and document["forge"] == native118.FORGE + require(document["minecraft"] == native119.MINECRAFT and document["forge"] == native119.FORGE and all(document["mods"].get(key) == value for key, value in mod_versions.items()), mode + ": checkpoint runtime differs from profile") return document @@ -91,7 +91,7 @@ def snapshot(station, label): results[mode] = dict(checkpoints=len(ledger), stationSnapshots=len(ledger) * len(endurance.CHECKED), setupToEndTicks=ticks, sampledTicks=sampled_ticks, sampledSeconds=sampled_seconds, sampledTicksPerSecond=sampled_ticks / sampled_seconds, outputs=dict(totals)) - return dict(status="PASS", inputReceiptSha256=digest(run / "native118-endurance-results.json"), profiles=results) + return dict(status="PASS", inputReceiptSha256=digest(run / "native119-endurance-results.json"), profiles=results) def main(): diff --git a/test-rig/migration/fixtures/native119/fml.toml b/test-rig/migration/fixtures/native119/fml.toml new file mode 100644 index 00000000..05c12555 --- /dev/null +++ b/test-rig/migration/fixtures/native119/fml.toml @@ -0,0 +1,6 @@ +# Forge 45.4.0 dedicated-server defaults, captured without mod jars. +versionCheck = true +splashscreen = true +disableOptimizedDFU = true +defaultConfigPath = "defaultconfigs" +maxThreads = -1 diff --git a/test-rig/migration/historical_test_inputs.py b/test-rig/migration/historical_test_inputs.py index c809bb05..89fedd23 100644 --- a/test-rig/migration/historical_test_inputs.py +++ b/test-rig/migration/historical_test_inputs.py @@ -18,7 +18,7 @@ # These named files now serve the active port. All other ledger entries still # check the worktree directly, so an unrelated edit cannot hide in the archive. ACTIVE_ADAPTERS = frozenset({ - STATIONS, "client-resources/resources/gui-label-expectations.json", + STATIONS, "README.md", "client-resources/resources/gui-label-expectations.json", *("migration/" + name + ".py" for name in ( "endurance", "extended_lifecycle", "lifecycle", "oil_transfer", "progression", "semantic_machine_audit", diff --git a/test-rig/migration/native117_broad.py b/test-rig/migration/native117_broad.py index bcc60bba..05f0d308 100644 --- a/test-rig/migration/native117_broad.py +++ b/test-rig/migration/native117_broad.py @@ -167,13 +167,15 @@ def authorize_broad(runtime): (runtime / name).touch() -def configure_broad(runtime, port): +def configure_broad(runtime, port, flat_settings=None): # Match the walkable WorksRunner rather than matrix.stage's migration-only # defaults. Clearing a freshly generated village can race native POI tasks. overrides = {"server-port": str(port), "generator-settings": "", "generate-structures": "false", "level-seed": "advantage-works-1.17.1-v1", "difficulty": "0", "gamemode": "creative", "allow-flight": "true", "enable-command-block": "true", "force-gamemode": "true", "spawn-animals": "false", "spawn-monsters": "false", "spawn-npcs": "false", "pvp": "false"} + if flat_settings is not None: + overrides["generator-settings"] = json.dumps(flat_settings, separators=(",", ":")) path = runtime / "server.properties" lines = [line for line in path.read_text().splitlines() if line.partition("=")[0] not in overrides] path.write_text("\n".join(lines) + "\n" + "".join(f"{key}={value}\n" for key, value in overrides.items())) @@ -200,7 +202,7 @@ class BroadServer(BroadCommands, Server): pass -def run_profile(args, profile, output, name, port, stations, *, native118_target=None): +def run_profile(args, profile, output, name, port, stations, *, native118_target=None, native_adapter=None): runtime = output / name if native118_target is None: if name == "packaged-decoupled": @@ -212,21 +214,22 @@ def run_profile(args, profile, output, name, port, stations, *, native118_target server_type, java = BroadServer, args.java8 versions = dict(minecraft="1.17.1", forge="37.1.1") else: - import native118 + if native_adapter is None: + import native118 as native_adapter - class NativeBroadServer(BroadCommands, native118.Server): + class NativeBroadServer(BroadCommands, native_adapter.Server): pass seed, code, entries = native118_target if name == "packaged-decoupled": entries = {key: value for key, value in entries.items() if key != "basemetals"} - launcher = native118.stage(runtime, (seed, code, entries), port) + launcher = native_adapter.stage(runtime, (seed, code, entries), port) pins = entries.values() - verify = lambda: native118.verify_runtime(runtime) - require_boot = native118.require_clean_log + verify = lambda: native_adapter.verify_runtime(runtime) + require_boot = native_adapter.require_clean_log server_type, java = NativeBroadServer, args.java17 - versions = dict(minecraft=native118.MINECRAFT, forge=native118.FORGE) - configure_broad(runtime, port) + versions = dict(minecraft=native_adapter.MINECRAFT, forge=native_adapter.FORGE) + configure_broad(runtime, port, getattr(native_adapter, "FLAT_SETTINGS", None)) authorize_broad(runtime) result = dict(status="FAIL", profile=name, port=port, phases={}) expected_mods = {pin["id"]: pin["version"] for pin in pins diff --git a/test-rig/migration/native117_broad_audit_fixtures.py b/test-rig/migration/native117_broad_audit_fixtures.py index b701a0d5..4e65c529 100644 --- a/test-rig/migration/native117_broad_audit_fixtures.py +++ b/test-rig/migration/native117_broad_audit_fixtures.py @@ -15,7 +15,7 @@ LINE = Path(__file__).resolve().parents[3].name.removeprefix("PA") # This is a historical audit fixture, not an attestation about the active port. -if LINE == "118": +if LINE in ("118", "119"): LINE = "117" diff --git a/test-rig/migration/native117_broad_result_audit.py b/test-rig/migration/native117_broad_result_audit.py index 2a339054..96d6b0bc 100644 --- a/test-rig/migration/native117_broad_result_audit.py +++ b/test-rig/migration/native117_broad_result_audit.py @@ -19,7 +19,8 @@ PROFILES = ("packaged-current", "packaged-decoupled") VERSIONS = {"113": ("1.13.2", "25.0.223"), "114": ("1.14.4", "28.2.26"), "115": ("1.15.2", "31.2.57"), "116": ("1.16.5", "36.2.34"), - "117": ("1.17.1", "37.1.1"), "118": ("1.18.2", "40.3.0")} + "117": ("1.17.1", "37.1.1"), "118": ("1.18.2", "40.3.0"), + "119": ("1.19.4", "45.4.0")} NATIVE117_LAUNCHERS = { "libraries/net/minecraftforge/forge/1.17.1-37.1.1/win_args.txt", "libraries/net/minecraftforge/forge/1.17.1-37.1.1/unix_args.txt", @@ -38,6 +39,20 @@ "libraries/cpw/mods/bootstraplauncher/1.0.0/bootstraplauncher-1.0.0.jar", "libraries/net/minecraft/server/1.18.2-20220404.173914/server-1.18.2-20220404.173914-extra.jar", } +NATIVE119_LAUNCHERS = { + "libraries/net/minecraftforge/forge/1.19.4-45.4.0/win_args.txt", + "libraries/net/minecraftforge/forge/1.19.4-45.4.0/unix_args.txt", +} +NATIVE119_REQUIRED_CODE = { + "libraries/net/minecraftforge/fmlloader/1.19.4-45.4.0/fmlloader-1.19.4-45.4.0.jar", + "libraries/cpw/mods/bootstraplauncher/1.1.2/bootstraplauncher-1.1.2.jar", + "libraries/net/minecraft/server/1.19.4-20230314.122934/server-1.19.4-20230314.122934-extra.jar", +} +MODULAR_CODE = { + "117": (NATIVE117_LAUNCHERS, NATIVE117_REQUIRED_CODE, ".117011"), + "118": (NATIVE118_LAUNCHERS, NATIVE118_REQUIRED_CODE, ".118021"), + "119": (NATIVE119_LAUNCHERS, NATIVE119_REQUIRED_CODE, ".119041"), +} STATIONS = tuple(["W-%02d" % n for n in range(1, 5)] + ["S-%02d" % n for n in range(1, 9)] + ["E-%02d" % n for n in range(1, 9)] + ["P-API-01"]) @@ -268,13 +283,16 @@ def log_evidence(evidence, path, line, phase, records): r"Advantage Works command failed") for row in text.splitlines(): require(not error.search(row), str(path) + ": native117 runtime error: " + row) - elif line == "118": - from native118 import log_findings - require("with MCP 20220404.173914" in text, str(path) + ": missing native118 MCP identity") + elif line in ("118", "119"): + if line == "118": + from native118 import log_findings, MCP + else: + from native119 import log_findings, MCP + require("with MCP " + MCP in text, str(path) + ": missing native" + line + " MCP identity") require("ThreadedAnvilChunkStorage: All dimensions are saved" in shutdown, str(path) + ": missing completed all-dimension shutdown") failures, diagnostics = log_findings(text) - require(not failures, str(path) + ": native118 runtime error: " + repr(failures[:5])) + require(not failures, str(path) + ": native" + line + " runtime error: " + repr(failures[:5])) if diagnostics: require(evidence.json(path.with_suffix(".platform-diagnostics.json")) == diagnostics, str(path) + ": changed or unrecorded Netty capability diagnostics") @@ -294,10 +312,9 @@ def staged_pins(evidence, runtime, pins, name, line=None): require(sha(evidence.read(runtime / pins["launcher"])) == pins["launcher_sha256"].upper(), name + ": staged launcher changed") sealed = pins.get("serverCode", {}).get("files") - if line in ("117", "118"): + if line in MODULAR_CODE: label = "native" + line - launchers, required = ((NATIVE117_LAUNCHERS, NATIVE117_REQUIRED_CODE) if line == "117" - else (NATIVE118_LAUNCHERS, NATIVE118_REQUIRED_CODE)) + launchers, required, suffix = MODULAR_CODE[line] require(pins["launcher"] in launchers, name + ": " + label + " requires the modular Forge launcher") require(isinstance(sealed, dict) and sealed, name + ": missing sealed " + label + " engine inventory") require(required | {pins["launcher"]} <= set(sealed), @@ -318,7 +335,7 @@ def staged_pins(evidence, runtime, pins, name, line=None): name + ": linked or escaping native117 engine file") import tomllib for pin in jars: - require(pin["version"].endswith(".117011" if line == "117" else ".118021"), + require(pin["version"].endswith(suffix), name + ": non-" + label + " mod version") with zipfile.ZipFile(runtime / "mods" / Path(pin["path"]).name) as archive: metadata = tomllib.loads(archive.read("META-INF/mods.toml").decode("utf-8")) @@ -385,7 +402,7 @@ def profile_audit(evidence, runtime, name, report, pins, definitions, line): for phase in ("first", "restarted")} result = dict(status="PASS", stations=21, checks=checks, pins=custody, shutdown=shutdown, manualProcedures="NOT_AUTOMATED_BY_BROAD_SWEEP") - if line in ("117", "118"): + if line in MODULAR_CODE: result["native" + line + "LogArchives"] = { phase: {kind: log_evidence(evidence, runtime / (kind + "-" + phase + ".log"), line, phase, records[phase]) for kind in ("latest", "debug")} diff --git a/test-rig/migration/native119.py b/test-rig/migration/native119.py new file mode 100644 index 00000000..472b7052 --- /dev/null +++ b/test-rig/migration/native119.py @@ -0,0 +1,187 @@ +"""Run the 1.19.4 destination with exact, disposable server inputs.""" + +import argparse +import json +import os +import re +from pathlib import Path + +from matrix import digest, stage as stage_server, write_json +from native117 import jar_identity, require_disjoint, runtime_error_lines +from native117_code import NativeLaunch, code_files + + +MINECRAFT = "1.19.4" +FORGE = "45.4.0" +FORGE_VERSION = f"{MINECRAFT}-{FORGE}" +MCP = "20230314.122934" +RIG = Path(__file__).resolve().parents[1] +LAUNCHER = f"libraries/net/minecraftforge/forge/{FORGE_VERSION}/" + ( + "win_args.txt" if os.name == "nt" else "unix_args.txt") +INVENTORY = "native119-code-inventory.json" +FML_DEFAULTS = RIG / "migration/fixtures/native119/fml.toml" +NETTY_PROBES = { + "direct buffer constructor: unavailable": "java.lang.UnsupportedOperationException: Reflective setAccessible(true) disabled", + "jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable": ( + "java.lang.IllegalAccessException: class io.netty.util.internal.PlatformDependent0$7"), +} + + +def log_findings(text): + """Separate Netty's DEBUG capability probes from runtime exceptions.""" + failures, diagnostics = [], [] + current_probe = None + for line in text.splitlines(): + header = re.fullmatch(r"\[[^\]]+\] \[[^\]]+/DEBUG\] " + r"\[io\.netty\.util\.internal\.PlatformDependent0/\]: (.*)", line) + if header and header[1] in NETTY_PROBES: + current_probe = header[1] + elif current_probe in NETTY_PROBES and line.startswith(NETTY_PROBES[current_probe]): + diagnostics.append({"probe": current_probe, "detail": line}) + current_probe = "frames" + elif current_probe == "frames" and line.startswith("\tat "): + continue + else: + current_probe = None + failures.extend(runtime_error_lines(line)) + return failures, diagnostics + + +def require_clean_log(path): + failures, diagnostics = log_findings(path.read_text(encoding="utf-8", errors="replace")) + if failures: + raise AssertionError(f"Native runtime errors in {path}: {failures[:5]}") + return diagnostics + + +def require_boot_identity(path): + text = path.read_text(encoding="utf-8", errors="replace") + for marker in (f"Starting minecraft server version {MINECRAFT}", + f"Forge mod loading, version {FORGE}, for MC {MINECRAFT} with MCP {MCP}", "Done ("): + if marker not in text: + raise AssertionError("Missing native 1.19.4 startup witness: " + marker) + + +def require_native_boot(path): + require_clean_log(path) + require_boot_identity(path) + + +def seal(seed, receipt): + if receipt.exists() or not (seed / LAUNCHER).is_file(): + raise ValueError("Supply an installed 1.19.4 server and a new inventory path") + require_disjoint(receipt, seed) + write_json(receipt, {"forge": FORGE_VERSION, "files": code_files(seed)}) + + +def read_target(profile, seed, inventory, inventory_hash): + seed = seed.resolve() + data = json.loads(profile.read_text(encoding="utf-8")) + if (data.get("minecraftVersion") != MINECRAFT or data.get("forge", {}).get("version") != FORGE_VERSION + or data.get("mode") != "packaged" or data.get("portStatus") != "PORTED_UNQUALIFIED" + or data.get("qualificationStatus") != "UNQUALIFIED"): + raise ValueError("Use a pinned, unqualified Forge 45.4.0 destination profile") + if digest(inventory) != inventory_hash.upper(): + raise AssertionError("Forge server inventory hash changed") + code = json.loads(inventory.read_text(encoding="utf-8")) + if code.get("forge") != FORGE_VERSION or code_files(seed) != code.get("files"): + raise AssertionError("Forge server does not match its sealed code") + entries = {} + for mod in data["mods"]: + jar = (RIG / mod["path"]).resolve() + identity = mod["id"] + if (identity in entries or not mod["version"].endswith(".119041") + or mod["file"] != jar.name or "deobf" in jar.name + or not re.fullmatch("[A-Fa-f0-9]{64}", mod["sha256"])): + raise ValueError(f"Invalid packaged 1.19.4 mod pin: {identity}") + if digest(jar) != mod["sha256"].upper(): + raise AssertionError(f"Pinned mod changed: {jar}") + jar_identity(jar, identity, mod["version"]) + entries[identity] = dict(mod, path=jar) + if not {"basepower", "basesteam", "baseelectric", "orespawn", "advantageworkstest"} <= entries.keys(): + raise ValueError("The combined test requires the three base mods, OreSpawn and Advantage Works") + return seed, code, entries + + +FLAT_SETTINGS = json.loads((RIG / "src/main/resources/assets/advantageworkstest/works/flat-generator.json") + .read_text(encoding="utf-8")) + + +def stage(runtime, target, port, *, normal=False): + seed, code, entries = target + for protected in (seed, *(entry["path"] for entry in entries.values())): + require_disjoint(runtime, protected) + expected = {entry["file"]: entry["sha256"].upper() for entry in entries.values()} + if {entry["file"]: digest(entry["path"]) for entry in entries.values()} != expected: + raise AssertionError("Pinned mod jar changed before staging") + launcher = stage_server(runtime, (seed, LAUNCHER, [entry["path"] for entry in entries.values()])) + staged = json.loads((runtime / "input-jars.json").read_text(encoding="utf-8")) + if {entry["file"]: entry["sha256"] for entry in staged} != expected: + raise AssertionError("Pinned mod jar changed during staging") + write_json(runtime / INVENTORY, code) + path = runtime / "server.properties" + overrides = {"level-type": "minecraft:normal" if normal else "minecraft:flat", + "generator-settings": "" if normal else json.dumps(FLAT_SETTINGS, separators=(",", ":")), "server-port": str(port), "generate-structures": "false", + "simulation-distance": "4", "allow-nether": "true"} + lines = [line for line in path.read_text().splitlines() if line.partition("=")[0] not in overrides] + path.write_text("\n".join(lines) + "\n" + "".join(f"{k}={v}\n" for k, v in overrides.items())) + verify_runtime(runtime) + return launcher + + +def verify_runtime(runtime): + code = json.loads((runtime / INVENTORY).read_text(encoding="utf-8")) + if code_files(runtime) != code["files"]: + raise AssertionError("Staged server code changed") + entries = json.loads((runtime / "input-jars.json").read_text(encoding="utf-8")) + if {f.name: digest(f) for f in (runtime / "mods").glob("*.jar")} != { + item["file"]: item["sha256"] for item in entries}: + raise AssertionError("Staged mod jars changed") + + +def prepare_fml_defaults(runtime): + # Avoid Forge's first-write/file-watcher race without touching imported settings. + config = runtime / "config/fml.toml" + config.parent.mkdir(exist_ok=True) + try: + with config.open("xb") as output: + output.write(FML_DEFAULTS.read_bytes()) + except FileExistsError: + return + write_json(runtime / "native119-fml-defaults.json", {"sha256": digest(FML_DEFAULTS), "forge": FORGE_VERSION}) + + +class Server(NativeLaunch): + forge_version = FORGE_VERSION + + def __init__(self, runtime, java, launcher, label): + if launcher != LAUNCHER: + raise ValueError("Wrong server launch target") + verify_runtime(runtime) + prepare_fml_defaults(runtime) + super().__init__(runtime, java, launcher, label) + + def validate_native_log(self, path): + diagnostics = require_clean_log(path) + if diagnostics: + write_json(path.with_suffix(".platform-diagnostics.json"), diagnostics) + + def close(self): + try: + super().close() + require_boot_identity(self.native_runtime / f"console-{self.native_label}.log") + finally: + verify_runtime(self.native_runtime) + + +def main(): + parser = argparse.ArgumentParser(description="Seal a Forge 45.4.0 test server's executable inputs.") + parser.add_argument("--seed", type=Path, required=True) + parser.add_argument("--inventory", type=Path, required=True) + args = parser.parse_args() + seal(args.seed.resolve(strict=True), args.inventory.resolve()) + print(digest(args.inventory)) + + +if __name__ == "__main__": + main() diff --git a/test-rig/migration/native118_actors.py b/test-rig/migration/native119_actors.py similarity index 89% rename from test-rig/migration/native118_actors.py rename to test-rig/migration/native119_actors.py index 37aee05c..94f9dff6 100644 --- a/test-rig/migration/native118_actors.py +++ b/test-rig/migration/native119_actors.py @@ -1,4 +1,4 @@ -"""Check retained player and dimension fixtures on the pinned Forge 40 server.""" +"""Check retained player and dimension fixtures on the pinned Forge 45 server.""" import argparse from functools import partial @@ -11,7 +11,7 @@ from native117_actors import run_route from native117_broad import read_json from native117_stopped_entities import audit_stopped -import native118 +import native119 def main(): @@ -33,22 +33,22 @@ def main(): before = fingerprint(source) if Path(approved["source"]).resolve() != source or approved["sourceFingerprint"] != before: raise AssertionError("Historical fixture no longer matches its approved source") - target = native118.read_target(args.profile, args.seed, args.inventory, args.inventory_sha256) + target = native119.read_target(args.profile, args.seed, args.inventory, args.inventory_sha256) with socket.socket() as port: port.bind(("127.0.0.1", args.port)) output.mkdir() result = dict(status="RUNNING", kind=args.kind, source=str(source), realClientLogin="NOT_TESTED", fixtureReceiptSha256=digest(args.fixture_receipt), profileSha256=digest(args.profile), inventorySha256=digest(args.inventory), checks={}) - report = output / "native118-actors-results.json" + report = output / "native119-actors-results.json" write_json(report, result) try: args.target_manifest = args.profile result["checks"] = run_route( - source, output / "server", target, args, stage=native118.stage, server_type=native118.Server, - stop_audit=partial(audit_stopped, minecraft_version=native118.MINECRAFT, data_version=2975, + source, output / "server", target, args, stage=native119.stage, server_type=native119.Server, + stop_audit=partial(audit_stopped, minecraft_version=native119.MINECRAFT, data_version=3337, stopping_phase="ServerStoppingEvent"), - check_log=native118.require_native_boot, minecraft=native118.MINECRAFT) + check_log=native119.require_native_boot, minecraft=native119.MINECRAFT) result["status"] = "PASS" if all(c["status"] == "PASS" for c in result["checks"].values()) else "FAIL" except Exception: result.update(status="FAIL", error=traceback.format_exc()) diff --git a/test-rig/migration/native118_broad.py b/test-rig/migration/native119_broad.py similarity index 85% rename from test-rig/migration/native118_broad.py rename to test-rig/migration/native119_broad.py index 770ad371..68a94423 100644 --- a/test-rig/migration/native118_broad.py +++ b/test-rig/migration/native119_broad.py @@ -1,4 +1,4 @@ -"""Commission and restart all 21 Works stations with the pinned 1.18.2 jars.""" +"""Commission and restart all 21 Works stations with the pinned 1.19.4 jars.""" import argparse from pathlib import Path @@ -8,7 +8,7 @@ from matrix import digest, write_json from native117 import output_path, require_disjoint from native117_broad import campaign_document, read_json, run_profile -import native118 +import native119 def freeze_mods(entries, destination): @@ -35,23 +35,23 @@ def main(): if not 1 <= args.run_seconds <= 60 or not 1024 <= args.port < 65535: parser.error("Bounded run duration and unprivileged consecutive ports required") output = output_path(args.output) - target = native118.read_target(args.profile, args.seed, args.inventory, args.inventory_sha256) + target = native119.read_target(args.profile, args.seed, args.inventory, args.inventory_sha256) if "basemetals" not in target[2]: raise ValueError("Use the complete profile; the decoupled profile is derived from it") for path in (args.profile, args.seed, args.inventory, *(item["path"] for item in target[2].values())): require_disjoint(output, path) - definition = native118.RIG / "src/main/resources/assets/advantageworkstest/works/works-1.13.2.json" + definition = native119.RIG / "src/main/resources/assets/advantageworkstest/works/works-1.13.2.json" stations = read_json(definition)["stations"] if len(stations) != 21: raise AssertionError("The complete 21-bay specification is required") output.mkdir(parents=True) - manifest = output / "target118.json" + manifest = output / "target119.json" seed, code, entries = target entries = freeze_mods(entries, output / "pinned-inputs") target = seed, code, entries - write_json(manifest, {"target118": { - "seed": str(seed), "launcher": native118.LAUNCHER, - "launcher_sha256": digest(seed / native118.LAUNCHER), "serverCode": code, + write_json(manifest, {"target119": { + "seed": str(seed), "launcher": native119.LAUNCHER, + "launcher_sha256": digest(seed / native119.LAUNCHER), "serverCode": code, "jars": [{**item, "path": str(item["path"])} for item in entries.values()], }}) runner = output / "runner-source.py" @@ -59,14 +59,14 @@ def main(): proof = dict(profileSha256=digest(args.profile), inventorySha256=digest(args.inventory), stationDefinitionSha256=digest(definition), runnerSha256=digest(runner), targetManifest=str(manifest), targetManifestSha256=digest(manifest), - minecraft=native118.MINECRAFT) + minecraft=native119.MINECRAFT) names = ("packaged-current", "packaged-decoupled") results = {} - report = output / "native118-broad-results.json" + report = output / "native119-broad-results.json" for index, name in enumerate(names): write_json(report, campaign_document(names, results, proof, finished=False)) try: - results[name] = run_profile(args, None, output, name, args.port + index, stations, native118_target=target) + results[name] = run_profile(args, None, output, name, args.port + index, stations, native118_target=target, native_adapter=native119) except Exception: results[name] = dict(status="FAIL", profile=name, error=traceback.format_exc()) print(name, results[name]["status"], flush=True) diff --git a/test-rig/migration/native118_endurance.py b/test-rig/migration/native119_endurance.py similarity index 92% rename from test-rig/migration/native118_endurance.py rename to test-rig/migration/native119_endurance.py index 674df303..776023f7 100644 --- a/test-rig/migration/native118_endurance.py +++ b/test-rig/migration/native119_endurance.py @@ -1,4 +1,4 @@ -"""Run the measured Works feed, conservation and cold-restart protocol on 1.18.2.""" +"""Run the measured Works feed, conservation and cold-restart protocol on 1.19.4.""" import argparse from collections import Counter @@ -11,11 +11,11 @@ from native117 import native_normalized, output_path, require_disjoint from native117_broad import read_json from native117_endurance import NativeEnduranceRig, configure_new_fresh_fixture, prove_fresh_fixture_before_build -import native118 +import native119 class Rig(NativeEnduranceRig): - server_type = native118.Server + server_type = native119.Server def main(): @@ -29,32 +29,32 @@ def main(): args = parser.parse_args() endurance.require(args.seconds >= 60 and args.seconds % 60 == 0, "Use whole one-minute batches") root = output_path(args.output) - target = native118.read_target(args.profile, args.seed, args.inventory, args.inventory_sha256) + target = native119.read_target(args.profile, args.seed, args.inventory, args.inventory_sha256) for protected in (args.profile, args.seed, args.inventory, *(item["path"] for item in target[2].values())): require_disjoint(root, protected) source_before = None if args.retained: require_disjoint(root, args.retained) - prior = read_json(args.retained / "native118-legacy-world-results.json") + prior = read_json(args.retained / "native119-legacy-world-results.json") if (prior["status"] != "PASS" or prior["profileSha256"] != digest(args.profile) or prior["inventorySha256"] != digest(args.inventory) or digest(args.retained / "migration-restarted.json") != prior["phases"]["restarted"]["snapshotSha256"]): raise AssertionError("Retained input does not match the passing pinned migration") source_before = fingerprint(args.retained / WORLD) - spec = native118.RIG / "src/main/resources/assets/advantageworkstest/works/works-1.13.2.json" + spec = native119.RIG / "src/main/resources/assets/advantageworkstest/works/works-1.13.2.json" definitions = {row["id"]: row for row in read_json(spec)["stations"]} - report = dict(status="RUNNING", minecraft=native118.MINECRAFT, duration_seconds=args.seconds, + report = dict(status="RUNNING", minecraft=native119.MINECRAFT, duration_seconds=args.seconds, interval_seconds=60, profileSha256=digest(args.profile), inventorySha256=digest(args.inventory), stationDefinitionSha256=digest(spec), active_stations=endurance.ACTIVE, checked_stations=endurance.CHECKED, profiles={}, retained=str(args.retained) if args.retained else None) - receipt = root / "native118-endurance-results.json" + receipt = root / "native119-endurance-results.json" write_json(receipt, report) rigs = [] try: - cases = (("fresh118", None), ("retained-upgrade-native118", args.retained)) if args.retained else (("fresh118", None),) + cases = (("fresh119", None), ("retained-upgrade-native119", args.retained)) if args.retained else (("fresh119", None),) for index, (name, source) in enumerate(cases): runtime = root / name - launcher = native118.stage(runtime, target, args.port + index) + launcher = native119.stage(runtime, target, args.port + index) rig = Rig(name, runtime, args.java17, launcher, definitions) rigs.append(rig) if source: @@ -134,7 +134,7 @@ def main(): for rig in rigs: try: rig.shutdown() - native118.verify_runtime(rig.runtime) + native119.verify_runtime(rig.runtime) except Exception as error: report.update(status="FAIL", shutdown_or_audit_error=repr(error)) if args.retained: diff --git a/test-rig/migration/native118_legacy_world.py b/test-rig/migration/native119_legacy_world.py similarity index 92% rename from test-rig/migration/native118_legacy_world.py rename to test-rig/migration/native119_legacy_world.py index 3fbbe991..6a82494d 100644 --- a/test-rig/migration/native118_legacy_world.py +++ b/test-rig/migration/native119_legacy_world.py @@ -1,4 +1,4 @@ -"""Compare original and released legacy fixtures against live and saved 1.18.2 state.""" +"""Compare original and released legacy fixtures against live and saved 1.19.4 state.""" import argparse from pathlib import Path @@ -11,8 +11,8 @@ from native117_broad import read_json from native117_commands import native from native117_legacy_world import turbine -import native118 -import native118_saved +import native119 +import native119_saved def portable(hashes): @@ -20,7 +20,7 @@ def portable(hashes): def operate(runtime, frozen, target, java, port, positions, expected): - launcher = native118.stage(runtime, target, port) + launcher = native119.stage(runtime, target, port) for path in (WORLD, "config"): shutil.copytree(frozen / path, runtime / path) shutil.copy2(frozen / "migration-fixture.json", runtime / "migration-fixture.json") @@ -28,7 +28,7 @@ def operate(runtime, frozen, target, java, port, positions, expected): jobs = (("sourceChest", "destination", "dirt", "dirt", 8), ("steamCrusher", "steamCrusher", "stone", "cobblestone", 1), ("electricFurnace", "electricFurnace", "iron_ore", "iron_ingot", 1)) - server = native118.Server(runtime, java, launcher, "operation") + server = native119.Server(runtime, java, launcher, "operation") try: for source, _, item, _, count in jobs: x, y, z = positions[source] @@ -79,10 +79,10 @@ def main(): if before != portable(previous["sourceFingerprint"]): raise AssertionError("Legacy source changed since the previous run") output = output_path(args.output) - target = native118.read_target(args.profile, args.seed, args.inventory, args.inventory_sha256) + target = native119.read_target(args.profile, args.seed, args.inventory, args.inventory_sha256) for protected in (source, args.profile, args.fixture, args.seed, args.inventory): require_disjoint(output, protected) - launcher = native118.stage(output, target, args.port) + launcher = native119.stage(output, target, args.port) shutil.copytree(source / WORLD, output / WORLD) shutil.copytree(source / "config", output / "config") if fingerprint(source / WORLD) != fingerprint(output / WORLD): @@ -92,11 +92,11 @@ def main(): result = dict(status="RUNNING", source=str(source), sourceFingerprint=before, priorReceiptSha256=digest(args.prior_run), profileSha256=digest(args.profile), inventorySha256=digest(args.inventory), phases={}) - report = output / "native118-legacy-world-results.json" + report = output / "native119-legacy-world-results.json" try: saves = {} for phase in ("converted", "restarted"): - server = native118.Server(output, args.java17, launcher, phase) + server = native119.Server(output, args.java17, launcher, phase) try: server.snapshot(phase) server.send("save-all flush") @@ -106,8 +106,8 @@ def main(): snapshot = output / f"migration-{phase}.json" rows = read_json(snapshot) verify_snapshot(expected, rows) - saves[phase] = native118_saved.capture(output, phase, rows) - observed = turbine(output / WORLD, positions["hydroGenerator"], data_version=native118_saved.DATA_VERSION) + saves[phase] = native119_saved.capture(output, phase, rows) + observed = turbine(output / WORLD, positions["hydroGenerator"], data_version=native119_saved.DATA_VERSION) if observed != dict(expected["turbine"], id="baseelectric:hydroturbine"): raise AssertionError("Saved turbine identity, position or parent changed") result["phases"][phase] = dict(status="PASS", witnesses=len(rows), turbine=observed, diff --git a/test-rig/migration/native118_machines.py b/test-rig/migration/native119_machines.py similarity index 82% rename from test-rig/migration/native118_machines.py rename to test-rig/migration/native119_machines.py index 679f9637..e6a2e59f 100644 --- a/test-rig/migration/native118_machines.py +++ b/test-rig/migration/native119_machines.py @@ -1,4 +1,4 @@ -"""Exercise 1.18.2 machines using the historical lifecycle and harvest oracles.""" +"""Exercise 1.19.4 machines using the historical lifecycle and harvest oracles.""" import argparse from pathlib import Path @@ -12,42 +12,42 @@ from native117_lifecycle import NativeLifecycleRun, authorize_lifecycle, exercise, read_json from native117_oil import NativeOilRun, exercise as exercise_oil, fixtures as oil_fixtures from native117_readiness import await_ready -import native118 +import native119 MINERALOGY = { "id": "mineralogy", - "version": "6.1.4.118021", - "sourceRef": "a5f019592420d3f0de5e23585b7ee75cde2e84b3", - "file": "Mineralogy-6.1.4.118021.jar", - "sha256": "30BA2DECBA56F79EE137A1C825E2F7E4D9299DB109FAFD30A2F66D26C45F2E74", + "version": "6.1.4.119041", + "sourceRef": "84222a0f129c086fd5ddcdb4244468f50ac0d4d1", + "file": "Mineralogy-6.1.4.119041.jar", + "sha256": "0E197D92B74F8466F35B31357E12629649EA58FF655CEFDF0D1991BE72DDA8A6", } class LifecycleRun(NativeLifecycleRun): - minecraft = native118.MINECRAFT + minecraft = native119.MINECRAFT max_ticket_distance = 12 - require_boot = staticmethod(native118.require_native_boot) + require_boot = staticmethod(native119.require_native_boot) def open_server(self, label): - return native118.Server(self.runtime, self.java, self.launcher, label) + return native119.Server(self.runtime, self.java, self.launcher, label) class OilRun(NativeOilRun): - expected_launcher = native118.LAUNCHER - require_boot = staticmethod(native118.require_native_boot) + expected_launcher = native119.LAUNCHER + require_boot = staticmethod(native119.require_native_boot) def open_server(self, label): - return native118.Server(self.runtime, self.java, self.launcher, label) + return native119.Server(self.runtime, self.java, self.launcher, label) def harvest(runtime, java, launcher, report, progress): mark_runtime(runtime) for label in ("exercise", "restored"): - server = native118.Server(runtime, java, launcher, label) + server = native119.Server(runtime, java, launcher, label) try: server.send("forceload add 1198 1022 1202 1026") ready = await_ready(server, runtime, HARVEST_CHUNKS, "harvest-" + label, - minecraft=native118.MINECRAFT) + minecraft=native119.MINECRAFT) server.send("advharvest " + label) server.wait("ADVHARVEST COMPLETE " + label, 120) document = read_json(runtime / "advantage-works-results" / f"native-harvest-{label}.json") @@ -55,7 +55,7 @@ def harvest(runtime, java, launcher, report, progress): or document["worldTime"] < ready["completedWorldTime"]): raise AssertionError("Harvest preceded native chunk/entity readiness") report[label] = {"observation": document, "readiness": ready} - require_checks(document, label, minecraft=native118.MINECRAFT) + require_checks(document, label, minecraft=native119.MINECRAFT) require_isolation(document) server.send("save-all flush") server.wait("Saved the game", 30) @@ -74,7 +74,7 @@ def main(): parser.add_argument("--campaign", choices=("lifecycle", "harvest", "oil"), required=True) parser.add_argument("--mineralogy-jar", type=Path, help="Include the pinned Mineralogy build in the oil test") args = parser.parse_args() - target = native118.read_target(args.profile, args.seed, args.inventory, args.inventory_sha256) + target = native119.read_target(args.profile, args.seed, args.inventory, args.inventory_sha256) if args.mineralogy_jar: jar = args.mineralogy_jar.resolve() if args.campaign != "oil" or jar.name != MINERALOGY["file"] or digest(jar) != MINERALOGY["sha256"]: @@ -88,9 +88,9 @@ def main(): Path(__file__).with_name(f"native117_{args.campaign}.py")] helper_hashes = {path.name: digest(path) for path in helpers} seed_hashes = fingerprint(target[0]) - launcher = native118.stage(output, target, args.port) - path = output / f"native118-{args.campaign}-results.json" - report = dict(status="RUNNING", minecraft=native118.MINECRAFT, forge=native118.FORGE, + launcher = native119.stage(output, target, args.port) + path = output / f"native119-{args.campaign}-results.json" + report = dict(status="RUNNING", minecraft=native119.MINECRAFT, forge=native119.FORGE, profileSha256=digest(args.profile), inventorySha256=digest(args.inventory), javaSha256=digest(args.java17), helperSha256=helper_hashes) @@ -116,7 +116,7 @@ def progress(values): write_json(output / "lifecycle-fixture.json", definition) authorize_lifecycle(output) runner = LifecycleRun(output, args.java17, launcher, definition) - result = exercise(runner, "fresh118", progress) + result = exercise(runner, "fresh119", progress) report.update(status=result["status"], observations=result) except Exception as error: report.update(status="FAIL", failure=repr(error), traceback=traceback.format_exc()) @@ -125,7 +125,7 @@ def progress(values): try: if runner is not None: runner.close() - native118.verify_runtime(output) + native119.verify_runtime(output) report["seedUnchanged"] = fingerprint(target[0]) == seed_hashes report["helpersUnchanged"] = {path.name: digest(path) for path in helpers} == helper_hashes if not report["seedUnchanged"] or not report["helpersUnchanged"]: diff --git a/test-rig/migration/native118_modern_upgrade.py b/test-rig/migration/native119_modern_upgrade.py similarity index 80% rename from test-rig/migration/native118_modern_upgrade.py rename to test-rig/migration/native119_modern_upgrade.py index bb96d72f..c8aa4ff0 100644 --- a/test-rig/migration/native118_modern_upgrade.py +++ b/test-rig/migration/native119_modern_upgrade.py @@ -1,4 +1,4 @@ -"""Load and operate a stopped, already-qualified 1.17.1 save on 1.18.2.""" +"""Load and operate a stopped, already-qualified 1.18.2 save on 1.19.4.""" import argparse from pathlib import Path @@ -8,9 +8,9 @@ from native117 import native_normalized, output_path, require_disjoint from native117_legacy_world import turbine from native117_lifecycle import read_json -import native118 -from native118_legacy_world import operate -import native118_saved +import native119 +from native119_legacy_world import operate +import native119_saved def main(): @@ -22,9 +22,9 @@ def main(): parser.add_argument("--port", type=int, required=True) args = parser.parse_args() source = args.source.resolve() - prior = source / "native117-legacy-world-results.json" + prior = source / "native118-legacy-world-results.json" if digest(prior) != args.receipt_sha256.upper(): - raise AssertionError("The protected 1.17.1 receipt changed") + raise AssertionError("The protected 1.18.2 receipt changed") receipt = read_json(prior) if receipt["status"] != "PASS" or not receipt["sourceUnchanged"]: raise AssertionError("Source lacks a passing independent migration receipt") @@ -41,25 +41,25 @@ def main(): before = fingerprint(source) positions = {row["name"]: [row["x"], row["y"], row["z"]] for row in read_json(source / "migration-fixture.json")} - target = native118.read_target(args.profile, args.seed, args.inventory, args.inventory_sha256) + target = native119.read_target(args.profile, args.seed, args.inventory, args.inventory_sha256) output = output_path(args.output) for path in (source, args.profile, args.seed, args.inventory, *(item["path"] for item in target[2].values())): require_disjoint(output, path) - launcher = native118.stage(output, target, args.port) + launcher = native119.stage(output, target, args.port) for path in (WORLD, "config"): shutil.copytree(source / path, output / path) if fingerprint(source / WORLD) != fingerprint(output / WORLD): - raise AssertionError("The staged 1.17.1 save differs from the source") + raise AssertionError("The staged 1.18.2 save differs from the source") shutil.copy2(source / "migration-fixture.json", output / "migration-fixture.json") (output / "advantage-migration.allow").touch() - result = dict(status="RUNNING", minecraft=native118.MINECRAFT, forge=native118.FORGE, + result = dict(status="RUNNING", minecraft=native119.MINECRAFT, forge=native119.FORGE, source=str(source), sourceReceiptSha256=digest(prior), sourceFingerprint=before, profileSha256=digest(args.profile), inventorySha256=digest(args.inventory), phases={}) - result_path = output / "native118-modern-upgrade-results.json" + result_path = output / "native119-modern-upgrade-results.json" try: for label in ("converted", "restarted"): - server = native118.Server(output, args.java17, launcher, label) + server = native119.Server(output, args.java17, launcher, label) try: server.snapshot(label) server.send("save-all flush") @@ -70,12 +70,12 @@ def main(): comparison = compare(historical, snapshot, native_normalized) result["phases"][label] = comparison if comparison["status"] != "PASS": - raise AssertionError("The native 1.17.1 witnesses changed in " + label) - saved = native118_saved.capture(output, label, read_json(snapshot)) + raise AssertionError("The native 1.18.2 witnesses changed in " + label) + saved = native119_saved.capture(output, label, read_json(snapshot)) observed = turbine(output / WORLD, positions["hydroGenerator"], - data_version=native118_saved.DATA_VERSION) + data_version=native119_saved.DATA_VERSION) if observed != phase["turbine"]: - raise AssertionError("The saved 1.17.1 turbine changed") + raise AssertionError("The saved 1.18.2 turbine changed") result["phases"][label].update(serialized=saved, turbine=observed, snapshotSha256=digest(snapshot)) write_json(result_path, result) diff --git a/test-rig/migration/native118_networks.py b/test-rig/migration/native119_networks.py similarity index 88% rename from test-rig/migration/native118_networks.py rename to test-rig/migration/native119_networks.py index 4c9d11bc..466a5208 100644 --- a/test-rig/migration/native118_networks.py +++ b/test-rig/migration/native119_networks.py @@ -1,4 +1,4 @@ -"""Run the original partial-network, bridge and native interop oracles on Forge 40.""" +"""Run the original partial-network, bridge and native interop oracles on Forge 45.""" import argparse from pathlib import Path @@ -14,8 +14,8 @@ from native117_partial import exercise_bridge, exercise_partial from native117_readiness import await_ready from native117_ticket_networks import NativeTicketExtendedRun -import native118 -from native118_machines import LifecycleRun +import native119 +from native119_machines import LifecycleRun class PartialRun(NativeTicketExtendedRun, LifecycleRun): @@ -29,11 +29,11 @@ def interop(runtime, launcher, java, progress): mark_runtime(runtime) observations = {} for phase in ("exercise", "restored"): - server = native118.Server(runtime, java, launcher, phase) + server = native119.Server(runtime, java, launcher, phase) try: server.send("forceload add 1023 1023 1100 1030") ready = await_ready(server, runtime, (63, 63, 68, 64), "interop-" + phase, - minecraft=native118.MINECRAFT) + minecraft=native119.MINECRAFT) server.send("advinterop " + phase) server.wait("ADVINTEROP COMPLETE " + phase, 120) report = read_json(runtime / "advantage-works-results" / f"native-interop-{phase}.json") @@ -56,16 +56,16 @@ def main(): parser.add_argument("--port", type=int, required=True) parser.add_argument("--campaign", choices=("partial", "bridge", "extended", "interop"), required=True) args = parser.parse_args() - target = native118.read_target(args.profile, args.seed, args.inventory, args.inventory_sha256) + target = native119.read_target(args.profile, args.seed, args.inventory, args.inventory_sha256) runtime = output_path(args.output) for path in (args.profile, args.seed, args.inventory, *(item["path"] for item in target[2].values())): require_disjoint(runtime, path) protected = fingerprint(target[0]) - launcher = native118.stage(runtime, target, args.port) - report = dict(status="RUNNING", minecraft=native118.MINECRAFT, forge=native118.FORGE, + launcher = native119.stage(runtime, target, args.port) + report = dict(status="RUNNING", minecraft=native119.MINECRAFT, forge=native119.FORGE, profileSha256=digest(args.profile), inventorySha256=digest(args.inventory), javaSha256=digest(args.java17), runnerSha256=digest(Path(__file__))) - receipt = runtime / f"native118-{args.campaign}-results.json" + receipt = runtime / f"native119-{args.campaign}-results.json" def progress(value): report["observations"] = value @@ -94,7 +94,7 @@ def progress(value): try: if runner is not None: runner.close() - native118.verify_runtime(runtime) + native119.verify_runtime(runtime) report["seedUnchanged"] = fingerprint(target[0]) == protected if not report["seedUnchanged"]: raise AssertionError("Protected server seed changed during the test") diff --git a/test-rig/migration/native118_recipes.py b/test-rig/migration/native119_recipes.py similarity index 82% rename from test-rig/migration/native118_recipes.py rename to test-rig/migration/native119_recipes.py index f218b1f0..ca81ff6c 100644 --- a/test-rig/migration/native118_recipes.py +++ b/test-rig/migration/native119_recipes.py @@ -1,4 +1,4 @@ -"""Run recipe discovery and survival progression against the 1.18.2 player jars.""" +"""Run recipe discovery and survival progression against the 1.19.4 player jars.""" import argparse from pathlib import Path @@ -6,25 +6,25 @@ from matrix import digest, fingerprint, write_json from native117 import output_path, require_disjoint from native117_recipes import MOD_COUNTS, RecipePruning, progression_campaign, recipe_fixture, unlock_campaign -import native118 +import native119 -class RecipeServer(RecipePruning, native118.Server): +class RecipeServer(RecipePruning, native119.Server): @staticmethod def errors(text): - return native118.log_findings(text)[0] + return native119.log_findings(text)[0] class Engine: - version = (native118.MINECRAFT, native118.FORGE) + version = (native119.MINECRAFT, native119.FORGE) pack_format = 9 - server = native118.Server + server = native119.Server recipe_server = RecipeServer - result_name = "native118-recipes-results.json" + result_name = "native119-recipes-results.json" @staticmethod def stage(runtime, target, port, campaign): - launcher = native118.stage(runtime, target, port) + launcher = native119.stage(runtime, target, port) (runtime / ("advantage-recipes.allow" if campaign == "unlock" else "advantage-progression.allow")).touch() if campaign == "progression": (runtime / "advantage-works.allow").touch() @@ -40,14 +40,14 @@ def main(): parser.add_argument("--port", type=int, default=25650) parser.add_argument("--campaign", choices=("all", "unlock", "progression"), default="all") args = parser.parse_args() - target = native118.read_target(args.profile, args.seed, args.inventory, args.inventory_sha256) + target = native119.read_target(args.profile, args.seed, args.inventory, args.inventory_sha256) root = output_path(args.output) for path in (args.profile, args.seed, args.inventory, *(item["path"] for item in target[2].values())): require_disjoint(root, path) definition, sources = recipe_fixture([item["path"] for item in target[2].values()]) entry = {"jars": [{**item, "path": str(item["path"])} for item in target[2].values()]} before = fingerprint(target[0]) - report = dict(status="RUNNING", minecraft=native118.MINECRAFT, forge=native118.FORGE, campaign=args.campaign, + report = dict(status="RUNNING", minecraft=native119.MINECRAFT, forge=native119.FORGE, campaign=args.campaign, profileSha256=digest(args.profile), inventorySha256=digest(args.inventory), javaSha256=digest(args.java17), target_jars=entry["jars"], recipe_inventory=MOD_COUNTS, unlock_boots=[], progression_cases=[]) @@ -56,11 +56,11 @@ def main(): if args.campaign in ("all", "unlock"): unlock_campaign(root, target, entry, args.java17, args.port, definition, report, engine=Engine) if len(report["unlock_boots"]) != 5: - raise AssertionError("Incomplete 1.18.2 unlock campaign") + raise AssertionError("Incomplete 1.19.4 unlock campaign") if args.campaign in ("all", "progression"): progression_campaign(root, target, entry, args.java17, args.port, report, engine=Engine) if len(report["progression_cases"]) != 18: - raise AssertionError("Incomplete 1.18.2 progression campaign") + raise AssertionError("Incomplete 1.19.4 progression campaign") report["status"] = "PASS" except BaseException as failure: report.update(status="FAIL", error=repr(failure)) diff --git a/test-rig/migration/native118_respawn.py b/test-rig/migration/native119_respawn.py similarity index 86% rename from test-rig/migration/native118_respawn.py rename to test-rig/migration/native119_respawn.py index ef272d91..79ec3353 100644 --- a/test-rig/migration/native118_respawn.py +++ b/test-rig/migration/native119_respawn.py @@ -1,4 +1,4 @@ -"""Run the retained twelve-case native death and respawn oracle on Forge 40.""" +"""Run the retained twelve-case native death and respawn oracle on Forge 45.""" import argparse from pathlib import Path @@ -6,7 +6,7 @@ from matrix import digest from native117 import output_path, require_disjoint from native117_respawn import read_json, run -import native118 +import native119 def main(): @@ -27,10 +27,10 @@ def main(): source = Path(record["path"]).resolve(strict=True) if source.name != args.route or digest(source / "dimension-fixture.json") != record["baseFixtureSha256"]: raise AssertionError("Respawn fixture identity or definition differs from prior receipt") - target = native118.read_target(args.profile, args.seed, args.inventory, args.inventory_sha256) + target = native119.read_target(args.profile, args.seed, args.inventory, args.inventory_sha256) args.fixtures, args.routes, args.target_manifest = source.parent, [source.name], args.profile - return run(args, target=target, stage=native118.stage, server_type=native118.Server, - check_log=native118.require_native_boot, approved_sources={source.name: record["fingerprint"]}) + return run(args, target=target, stage=native119.stage, server_type=native119.Server, + check_log=native119.require_native_boot, approved_sources={source.name: record["fingerprint"]}) if __name__ == "__main__": diff --git a/test-rig/migration/native118_saved.py b/test-rig/migration/native119_saved.py similarity index 92% rename from test-rig/migration/native118_saved.py rename to test-rig/migration/native119_saved.py index 59e2670f..ef30312e 100644 --- a/test-rig/migration/native118_saved.py +++ b/test-rig/migration/native119_saved.py @@ -1,4 +1,4 @@ -"""Verify the actual flattened terrain and block entity data written by 1.18.2.""" +"""Verify the actual flattened terrain and block entity data written by 1.19.4.""" import gzip import io @@ -13,18 +13,18 @@ from transformer_order import read_region -DATA_VERSION = 2975 +DATA_VERSION = 3337 def decode_chunk(payload, coordinates): if len(payload) < 6 or int.from_bytes(payload[:4], "big") != len(payload) - 4: - raise ValueError("Malformed 1.18 chunk length") + raise ValueError("Malformed 1.19 chunk length") decoders = {1: gzip.decompress, 2: zlib.decompress, 3: lambda data: data} if payload[4] not in decoders: raise ValueError("Unknown chunk compression") chunk = plain(nbtlib.File.parse(io.BytesIO(decoders[payload[4]](payload[5:]))).unpack()) if chunk.get("DataVersion") != DATA_VERSION or "Level" in chunk: - raise ValueError("Chunk was not written in native 1.18.2 format") + raise ValueError("Chunk was not written in native 1.19.4 format") if (chunk["xPos"], chunk["zPos"]) != coordinates: raise ValueError("Saved chunk coordinates do not match the region slot") sections = {} @@ -45,12 +45,12 @@ def decode_chunk(payload, coordinates): def capture(runtime, label, rows): world = runtime / WORLD - evidence = runtime / "native118-save" / label + evidence = runtime / "native119-save" / label evidence.mkdir(parents=True) shutil.copy2(world / "level.dat", evidence / "level.dat") level = nbtlib.load(evidence / "level.dat")["Data"] if level["DataVersion"] != DATA_VERSION: - raise AssertionError("Saved level does not identify 1.18.2") + raise AssertionError("Saved level does not identify 1.19.4") regions, chunks = {}, {} for row in rows: x, y, z = pos = tuple(int(row[field]) for field in ("x", "y", "z")) diff --git a/test-rig/migration/native118_wolf_control.py b/test-rig/migration/native119_wolf_control.py similarity index 88% rename from test-rig/migration/native118_wolf_control.py rename to test-rig/migration/native119_wolf_control.py index 92cafa3a..51306a2e 100644 --- a/test-rig/migration/native118_wolf_control.py +++ b/test-rig/migration/native119_wolf_control.py @@ -7,7 +7,7 @@ import nbtlib from matrix import WORLD, digest, plain, write_json from native117 import output_path, require_disjoint -import native118 +import native119 CASES = (("tame8", 8, True, 20), ("tame20", 20, True, 20), ("wild8", 8, False, 8)) @@ -42,15 +42,15 @@ def main(): output = output_path(args.output) for path in (args.profile, args.seed, args.inventory): require_disjoint(output, path) - seed, inventory, _ = native118.read_target(args.profile, args.seed, args.inventory, args.inventory_sha256) + seed, inventory, _ = native119.read_target(args.profile, args.seed, args.inventory, args.inventory_sha256) with socket.socket() as port: port.bind(("127.0.0.1", args.port)) - launcher = native118.stage(output, (seed, inventory, {}), args.port, normal=True) - report = dict(status="RUNNING", scope="FRESH_FORGE_40_NO_MOD_JARS", nativeDataVersion=2975, + launcher = native119.stage(output, (seed, inventory, {}), args.port, normal=True) + report = dict(status="RUNNING", scope="FRESH_FORGE_45_NO_MOD_JARS", nativeDataVersion=3337, inventorySha256=digest(args.inventory), boots={}) try: for phase in ("summon", "restarted"): - server = native118.Server(output, args.java17, launcher, phase) + server = native119.Server(output, args.java17, launcher, phase) try: server.send("forceload add 0 0") server.send("say WOLF-CONTROL-CHUNK-TICKET") @@ -73,15 +73,15 @@ def main(): finally: server.close() report["boots"][phase] = observed - if int(nbtlib.load(output / WORLD / "level.dat")["Data"]["DataVersion"]) != 2975: - raise AssertionError("The control world is not native 1.18.2") + if int(nbtlib.load(output / WORLD / "level.dat")["Data"]["DataVersion"]) != 3337: + raise AssertionError("The control world is not native 1.19.4") report["status"] = "PASS" except BaseException as error: report.update(status="FAIL", error=repr(error)) raise finally: - write_json(output / "native118-wolf-control.json", report) - print("PASS: Forge 40 without any mods reproduced the native tame-wolf heal") + write_json(output / "native119-wolf-control.json", report) + print("PASS: Forge 45 without any mods reproduced the native tame-wolf heal") if __name__ == "__main__": diff --git a/test-rig/migration/test_native117_geometry.py b/test-rig/migration/test_native117_geometry.py index 53b9b611..6fefff38 100644 --- a/test-rig/migration/test_native117_geometry.py +++ b/test-rig/migration/test_native117_geometry.py @@ -238,7 +238,9 @@ def test_native_client_adaptations_match_the_approved_preservation_inputs(self): def test_active_port_adapters_are_separately_guarded(self): for name in sorted(ACTIVE_ADAPTERS): with self.subTest(path=name): - self.assertEqual(git_blob(BASE117_ADAPTERS, name).replace(b"\r\n", b"\n"), + # The active overview was revised in the audited 1.18 harness port. + baseline = "edc43a9" if name == "README.md" else BASE117_ADAPTERS + self.assertEqual(git_blob(baseline, name).replace(b"\r\n", b"\n"), (RIG / name).read_bytes().replace(b"\r\n", b"\n")) def test_ledger_cannot_refingerprint_or_exempt_historical_oracles(self): diff --git a/test-rig/migration/test_native119_log_policy.py b/test-rig/migration/test_native119_log_policy.py new file mode 100644 index 00000000..c36a6102 --- /dev/null +++ b/test-rig/migration/test_native119_log_policy.py @@ -0,0 +1,44 @@ +from pathlib import Path +import tempfile +import tomllib +import unittest + +from native119 import log_findings, prepare_fml_defaults + + +PROBE = "jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable" +DEBUG = "[09Nov2026 15:22:51.470] [main/DEBUG] [io.netty.util.internal.PlatformDependent0/]: " +DETAIL = ("java.lang.IllegalAccessException: class io.netty.util.internal.PlatformDependent0$7 " + "(in module io.netty.common) cannot access class jdk.internal.misc.Unsafe (in module java.base)") +FRAME = "\tat io.netty.util.internal.PlatformDependent0$7.run(PlatformDependent0.java:410)" + + +class Native119LogPolicyTests(unittest.TestCase): + def test_records_exact_netty_debug_fallback(self): + errors, diagnostics = log_findings("\n".join((DEBUG + PROBE, DETAIL, FRAME))) + self.assertEqual([], errors) + self.assertEqual([dict(probe=PROBE, detail=DETAIL)], diagnostics) + + def test_does_not_discard_same_exception_at_wrong_level_or_after_a_different_probe(self): + for header in (DEBUG.replace("/DEBUG]", "/ERROR]") + PROBE, DEBUG + "unrelated probe", ""): + errors, diagnostics = log_findings(header + "\n" + DETAIL) + self.assertIn(DETAIL, errors) + self.assertEqual([], diagnostics) + + def test_preserves_machine_failure_after_netty_probe(self): + error = "[15:22:53] [Server thread/ERROR] [minecraft/MinecraftServer]: Failed machine tick" + errors, _ = log_findings("\n".join((DEBUG + PROBE, DETAIL, FRAME, error))) + self.assertEqual([error], errors) + + def test_disposable_first_start_uses_forge_defaults_but_preserves_existing_config(self): + with tempfile.TemporaryDirectory() as directory: + runtime = Path(directory) + prepare_fml_defaults(runtime) + config = runtime / "config/fml.toml" + self.assertEqual({"versionCheck": True, "splashscreen": True, "disableOptimizedDFU": True, + "defaultConfigPath": "defaultconfigs", "maxThreads": -1}, + tomllib.loads(config.read_text(encoding="utf-8"))) + original = b'maxThreads = 2\n' + config.write_bytes(original) + prepare_fml_defaults(runtime) + self.assertEqual(original, config.read_bytes()) diff --git a/test-rig/profiles/native119/dev-core-apocalyptic.json b/test-rig/profiles/native119/dev-core-apocalyptic.json new file mode 100644 index 00000000..d8e478a4 --- /dev/null +++ b/test-rig/profiles/native119/dev-core-apocalyptic.json @@ -0,0 +1,44 @@ +{ + "name": "dev-core-apocalyptic", + "mode": "development", + "runtimeBase": "build/works-native119", + "minecraftVersion": "1.19.4", + "portStatus": "PORTED_UNQUALIFIED", + "qualificationStatus": "UNQUALIFIED", + "sourceCommit": "c9995315e2aee9d9bffec8eb8b1191ad8a0faeb7", + "sourceVersion": "2.4.11.119041", + "powerDeobfSha256": "512FDEC9275B33533EF773C110E6229E0F07268775FAD4B5942A15B68A9EB87D", + "levelType": "minecraft:flat", + "levelSeed": "advantage-works-1.19.4-v1", + "startupTimeoutSeconds": 180, + "forge": { + "version": "1.19.4-45.4.0", + "installerPath": "../../../../BaseMetals/.runtime-cache/forge-1.19.4-45.4.0-installer.jar", + "url": "https://maven.minecraftforge.net/net/minecraftforge/forge/1.19.4-45.4.0/forge-1.19.4-45.4.0-installer.jar", + "sha256": "6643BB8C21F409CC528FF656DAF60C206D304CC77BF529BBBAB4B336004DFC53" + }, + "mods": [ + { + "id": "basesteam", + "version": "2.2.12.119041", + "file": "baseSteam-2.2.12.119041-deobf.jar", + "path": "../../SteamAdvantage/SteamAdvantage/build/libs-dev/baseSteam-2.2.12.119041-deobf.jar", + "sha256": "B93702B618F961F59FA73EFD12DDE8C4E9D3D7697CFCC3CBAA818F0A76623725" + }, + { + "id": "baseelectric", + "version": "2.2.12.119041", + "file": "baseElectric-2.2.12.119041-deobf.jar", + "path": "../../ElectricAdvantage/ElectricAdvantage/build/libs-dev/baseElectric-2.2.12.119041-deobf.jar", + "sha256": "7D331A8227CBE1C7D84D405CFD5B6C69EC19BCE1AB4B2AF036B899E9CE1BD0CF" + }, + { + "id": "advantageworkstest", + "version": "1.1.3.119041", + "file": "AdvantageWorksTestHarness-1.1.3.119041-deobf.jar", + "path": "build/gradle/libs-dev/AdvantageWorksTestHarness-1.1.3.119041-deobf.jar", + "sha256": "17B02F5CA11B69EB6256BCDE2E13D62324A48CFE28C6873DBFDCDE7C271791DA" + } + ], + "recipeMode": "APOCALYPTIC" +} diff --git a/test-rig/profiles/native119/dev-core-tech-progression.json b/test-rig/profiles/native119/dev-core-tech-progression.json new file mode 100644 index 00000000..f8c483e0 --- /dev/null +++ b/test-rig/profiles/native119/dev-core-tech-progression.json @@ -0,0 +1,44 @@ +{ + "name": "dev-core-tech-progression", + "mode": "development", + "runtimeBase": "build/works-native119", + "minecraftVersion": "1.19.4", + "portStatus": "PORTED_UNQUALIFIED", + "qualificationStatus": "UNQUALIFIED", + "sourceCommit": "c9995315e2aee9d9bffec8eb8b1191ad8a0faeb7", + "sourceVersion": "2.4.11.119041", + "powerDeobfSha256": "512FDEC9275B33533EF773C110E6229E0F07268775FAD4B5942A15B68A9EB87D", + "levelType": "minecraft:flat", + "levelSeed": "advantage-works-1.19.4-v1", + "startupTimeoutSeconds": 180, + "forge": { + "version": "1.19.4-45.4.0", + "installerPath": "../../../../BaseMetals/.runtime-cache/forge-1.19.4-45.4.0-installer.jar", + "url": "https://maven.minecraftforge.net/net/minecraftforge/forge/1.19.4-45.4.0/forge-1.19.4-45.4.0-installer.jar", + "sha256": "6643BB8C21F409CC528FF656DAF60C206D304CC77BF529BBBAB4B336004DFC53" + }, + "mods": [ + { + "id": "basesteam", + "version": "2.2.12.119041", + "file": "baseSteam-2.2.12.119041-deobf.jar", + "path": "../../SteamAdvantage/SteamAdvantage/build/libs-dev/baseSteam-2.2.12.119041-deobf.jar", + "sha256": "B93702B618F961F59FA73EFD12DDE8C4E9D3D7697CFCC3CBAA818F0A76623725" + }, + { + "id": "baseelectric", + "version": "2.2.12.119041", + "file": "baseElectric-2.2.12.119041-deobf.jar", + "path": "../../ElectricAdvantage/ElectricAdvantage/build/libs-dev/baseElectric-2.2.12.119041-deobf.jar", + "sha256": "7D331A8227CBE1C7D84D405CFD5B6C69EC19BCE1AB4B2AF036B899E9CE1BD0CF" + }, + { + "id": "advantageworkstest", + "version": "1.1.3.119041", + "file": "AdvantageWorksTestHarness-1.1.3.119041-deobf.jar", + "path": "build/gradle/libs-dev/AdvantageWorksTestHarness-1.1.3.119041-deobf.jar", + "sha256": "17B02F5CA11B69EB6256BCDE2E13D62324A48CFE28C6873DBFDCDE7C271791DA" + } + ], + "recipeMode": "TECH_PROGRESSION" +} diff --git a/test-rig/profiles/native119/dev-core.json b/test-rig/profiles/native119/dev-core.json new file mode 100644 index 00000000..557062f3 --- /dev/null +++ b/test-rig/profiles/native119/dev-core.json @@ -0,0 +1,43 @@ +{ + "name": "dev-core", + "mode": "development", + "runtimeBase": "build/works-native119", + "minecraftVersion": "1.19.4", + "portStatus": "PORTED_UNQUALIFIED", + "qualificationStatus": "UNQUALIFIED", + "sourceCommit": "c9995315e2aee9d9bffec8eb8b1191ad8a0faeb7", + "sourceVersion": "2.4.11.119041", + "powerDeobfSha256": "512FDEC9275B33533EF773C110E6229E0F07268775FAD4B5942A15B68A9EB87D", + "levelType": "minecraft:flat", + "levelSeed": "advantage-works-1.19.4-v1", + "startupTimeoutSeconds": 180, + "forge": { + "version": "1.19.4-45.4.0", + "installerPath": "../../../../BaseMetals/.runtime-cache/forge-1.19.4-45.4.0-installer.jar", + "url": "https://maven.minecraftforge.net/net/minecraftforge/forge/1.19.4-45.4.0/forge-1.19.4-45.4.0-installer.jar", + "sha256": "6643BB8C21F409CC528FF656DAF60C206D304CC77BF529BBBAB4B336004DFC53" + }, + "mods": [ + { + "id": "basesteam", + "version": "2.2.12.119041", + "file": "baseSteam-2.2.12.119041-deobf.jar", + "path": "../../SteamAdvantage/SteamAdvantage/build/libs-dev/baseSteam-2.2.12.119041-deobf.jar", + "sha256": "B93702B618F961F59FA73EFD12DDE8C4E9D3D7697CFCC3CBAA818F0A76623725" + }, + { + "id": "baseelectric", + "version": "2.2.12.119041", + "file": "baseElectric-2.2.12.119041-deobf.jar", + "path": "../../ElectricAdvantage/ElectricAdvantage/build/libs-dev/baseElectric-2.2.12.119041-deobf.jar", + "sha256": "7D331A8227CBE1C7D84D405CFD5B6C69EC19BCE1AB4B2AF036B899E9CE1BD0CF" + }, + { + "id": "advantageworkstest", + "version": "1.1.3.119041", + "file": "AdvantageWorksTestHarness-1.1.3.119041-deobf.jar", + "path": "build/gradle/libs-dev/AdvantageWorksTestHarness-1.1.3.119041-deobf.jar", + "sha256": "17B02F5CA11B69EB6256BCDE2E13D62324A48CFE28C6873DBFDCDE7C271791DA" + } + ] +} diff --git a/test-rig/profiles/native119/packaged-current.json b/test-rig/profiles/native119/packaged-current.json new file mode 100644 index 00000000..94eaf78b --- /dev/null +++ b/test-rig/profiles/native119/packaged-current.json @@ -0,0 +1,64 @@ +{ + "name": "packaged-current", + "mode": "packaged", + "runtimeBase": "build/works-native119", + "minecraftVersion": "1.19.4", + "portStatus": "PORTED_UNQUALIFIED", + "qualificationStatus": "UNQUALIFIED", + "sourceCommit": "c9995315e2aee9d9bffec8eb8b1191ad8a0faeb7", + "sourceVersion": "2.4.11.119041", + "powerDeobfSha256": "512FDEC9275B33533EF773C110E6229E0F07268775FAD4B5942A15B68A9EB87D", + "levelType": "minecraft:flat", + "levelSeed": "advantage-works-1.19.4-v1", + "startupTimeoutSeconds": 180, + "forge": { + "version": "1.19.4-45.4.0", + "installerPath": "../../../../BaseMetals/.runtime-cache/forge-1.19.4-45.4.0-installer.jar", + "url": "https://maven.minecraftforge.net/net/minecraftforge/forge/1.19.4-45.4.0/forge-1.19.4-45.4.0-installer.jar", + "sha256": "6643BB8C21F409CC528FF656DAF60C206D304CC77BF529BBBAB4B336004DFC53" + }, + "mods": [ + { + "id": "basepower", + "version": "2.4.11.119041", + "file": "basePower-2.4.11.119041.jar", + "path": "../build/libs/basePower-2.4.11.119041.jar", + "sha256": "E1689C1147F6F6F93F6516D9188A279093D6CCC2FF1FBEB506468B876BA03E60" + }, + { + "id": "basesteam", + "version": "2.2.12.119041", + "file": "baseSteam-2.2.12.119041.jar", + "path": "../../SteamAdvantage/SteamAdvantage/build/libs/baseSteam-2.2.12.119041.jar", + "sha256": "13E29CDFCA22BCEE30E6F7260C5DC11524C405BDF5DB16731CD6328C06AC548D" + }, + { + "id": "baseelectric", + "version": "2.2.12.119041", + "file": "baseElectric-2.2.12.119041.jar", + "path": "../../ElectricAdvantage/ElectricAdvantage/build/libs/baseElectric-2.2.12.119041.jar", + "sha256": "0064D563F9E83E126B6E57D107F8BCEF5AD80276EF422BFBCDB702A0CAD38C1A" + }, + { + "id": "basemetals", + "version": "3.1.2.119041", + "file": "BaseMetals-3.1.2.119041.jar", + "path": "../../../../BaseMetals/BaseMetals 119/BaseMetals/build/libs/BaseMetals-3.1.2.119041.jar", + "sha256": "991EA843EEDF5FA1E42E9984A07573C7D03D74230482230440E44EDCA895FA39" + }, + { + "id": "orespawn", + "version": "4.1.0.119041", + "file": "OreSpawn-4.1.0.119041.jar", + "path": "../../../../MinecraftMineralogy/OS119/OreSpawn/build/libs/OreSpawn-4.1.0.119041.jar", + "sha256": "1B2ECAA546E7FFB35863AC1D9FF92504471EE857045DCD01A068C5CA7F53CFFD" + }, + { + "id": "advantageworkstest", + "version": "1.1.3.119041", + "file": "AdvantageWorksTestHarness-1.1.3.119041.jar", + "path": "build/gradle/libs/AdvantageWorksTestHarness-1.1.3.119041.jar", + "sha256": "483E3E73568ADD9150084F6A2AFBAFCBB44666553B289066955D718D2CB0DA23" + } + ] +} diff --git a/test-rig/profiles/native119/packaged-decoupled.json b/test-rig/profiles/native119/packaged-decoupled.json new file mode 100644 index 00000000..98a5e31a --- /dev/null +++ b/test-rig/profiles/native119/packaged-decoupled.json @@ -0,0 +1,57 @@ +{ + "name": "packaged-decoupled", + "mode": "packaged", + "runtimeBase": "build/works-native119", + "minecraftVersion": "1.19.4", + "portStatus": "PORTED_UNQUALIFIED", + "qualificationStatus": "UNQUALIFIED", + "sourceCommit": "c9995315e2aee9d9bffec8eb8b1191ad8a0faeb7", + "sourceVersion": "2.4.11.119041", + "powerDeobfSha256": "512FDEC9275B33533EF773C110E6229E0F07268775FAD4B5942A15B68A9EB87D", + "levelType": "minecraft:flat", + "levelSeed": "advantage-works-1.19.4-v1", + "startupTimeoutSeconds": 180, + "forge": { + "version": "1.19.4-45.4.0", + "installerPath": "../../../../BaseMetals/.runtime-cache/forge-1.19.4-45.4.0-installer.jar", + "url": "https://maven.minecraftforge.net/net/minecraftforge/forge/1.19.4-45.4.0/forge-1.19.4-45.4.0-installer.jar", + "sha256": "6643BB8C21F409CC528FF656DAF60C206D304CC77BF529BBBAB4B336004DFC53" + }, + "mods": [ + { + "id": "basepower", + "version": "2.4.11.119041", + "file": "basePower-2.4.11.119041.jar", + "path": "../build/libs/basePower-2.4.11.119041.jar", + "sha256": "E1689C1147F6F6F93F6516D9188A279093D6CCC2FF1FBEB506468B876BA03E60" + }, + { + "id": "basesteam", + "version": "2.2.12.119041", + "file": "baseSteam-2.2.12.119041.jar", + "path": "../../SteamAdvantage/SteamAdvantage/build/libs/baseSteam-2.2.12.119041.jar", + "sha256": "13E29CDFCA22BCEE30E6F7260C5DC11524C405BDF5DB16731CD6328C06AC548D" + }, + { + "id": "baseelectric", + "version": "2.2.12.119041", + "file": "baseElectric-2.2.12.119041.jar", + "path": "../../ElectricAdvantage/ElectricAdvantage/build/libs/baseElectric-2.2.12.119041.jar", + "sha256": "0064D563F9E83E126B6E57D107F8BCEF5AD80276EF422BFBCDB702A0CAD38C1A" + }, + { + "id": "orespawn", + "version": "4.1.0.119041", + "file": "OreSpawn-4.1.0.119041.jar", + "path": "../../../../MinecraftMineralogy/OS119/OreSpawn/build/libs/OreSpawn-4.1.0.119041.jar", + "sha256": "1B2ECAA546E7FFB35863AC1D9FF92504471EE857045DCD01A068C5CA7F53CFFD" + }, + { + "id": "advantageworkstest", + "version": "1.1.3.119041", + "file": "AdvantageWorksTestHarness-1.1.3.119041.jar", + "path": "build/gradle/libs/AdvantageWorksTestHarness-1.1.3.119041.jar", + "sha256": "483E3E73568ADD9150084F6A2AFBAFCBB44666553B289066955D718D2CB0DA23" + } + ] +} diff --git a/test-rig/profiles/native119/packaged-mineralogy.json b/test-rig/profiles/native119/packaged-mineralogy.json new file mode 100644 index 00000000..4cfe222d --- /dev/null +++ b/test-rig/profiles/native119/packaged-mineralogy.json @@ -0,0 +1,69 @@ +{ + "name": "packaged-mineralogy", + "mode": "packaged", + "runtimeBase": "build/works-native119", + "minecraftVersion": "1.19.4", + "portStatus": "PORTED_UNQUALIFIED", + "qualificationStatus": "UNQUALIFIED", + "sourceCommit": "c9995315e2aee9d9bffec8eb8b1191ad8a0faeb7", + "sourceVersion": "2.4.11.119041", + "powerDeobfSha256": "512FDEC9275B33533EF773C110E6229E0F07268775FAD4B5942A15B68A9EB87D", + "levelType": "minecraft:flat", + "levelSeed": "advantage-works-1.19.4-v1", + "startupTimeoutSeconds": 180, + "forge": { + "version": "1.19.4-45.4.0", + "installerPath": "../../../../BaseMetals/.runtime-cache/forge-1.19.4-45.4.0-installer.jar", + "url": "https://maven.minecraftforge.net/net/minecraftforge/forge/1.19.4-45.4.0/forge-1.19.4-45.4.0-installer.jar", + "sha256": "6643BB8C21F409CC528FF656DAF60C206D304CC77BF529BBBAB4B336004DFC53" + }, + "mods": [ + { + "id": "basepower", + "version": "2.4.11.119041", + "file": "basePower-2.4.11.119041.jar", + "path": "../build/libs/basePower-2.4.11.119041.jar", + "sha256": "E1689C1147F6F6F93F6516D9188A279093D6CCC2FF1FBEB506468B876BA03E60" + }, + { + "id": "basesteam", + "version": "2.2.12.119041", + "file": "baseSteam-2.2.12.119041.jar", + "path": "../../SteamAdvantage/SteamAdvantage/build/libs/baseSteam-2.2.12.119041.jar", + "sha256": "13E29CDFCA22BCEE30E6F7260C5DC11524C405BDF5DB16731CD6328C06AC548D" + }, + { + "id": "baseelectric", + "version": "2.2.12.119041", + "file": "baseElectric-2.2.12.119041.jar", + "path": "../../ElectricAdvantage/ElectricAdvantage/build/libs/baseElectric-2.2.12.119041.jar", + "sha256": "0064D563F9E83E126B6E57D107F8BCEF5AD80276EF422BFBCDB702A0CAD38C1A" + }, + { + "id": "orespawn", + "version": "4.1.0.119041", + "file": "OreSpawn-4.1.0.119041.jar", + "path": "../../../../MinecraftMineralogy/OS119/OreSpawn/build/libs/OreSpawn-4.1.0.119041.jar", + "sha256": "1B2ECAA546E7FFB35863AC1D9FF92504471EE857045DCD01A068C5CA7F53CFFD" + }, + { + "id": "advantageworkstest", + "version": "1.1.3.119041", + "file": "AdvantageWorksTestHarness-1.1.3.119041.jar", + "path": "build/gradle/libs/AdvantageWorksTestHarness-1.1.3.119041.jar", + "sha256": "483E3E73568ADD9150084F6A2AFBAFCBB44666553B289066955D718D2CB0DA23" + }, + { + "id": "mineralogy", + "version": "6.1.4.119041", + "sourceRef": "84222a0f129c086fd5ddcdb4244468f50ac0d4d1", + "file": "Mineralogy-6.1.4.119041.jar", + "path": "../../../../MinecraftMineralogy/MinecraftMineralogy 119/MinecraftMineralogy/build/libs/Mineralogy-6.1.4.119041.jar", + "sha256": "0E197D92B74F8466F35B31357E12629649EA58FF655CEFDF0D1991BE72DDA8A6" + } + ], + "lifecycleStations": [ + "W-03", + "S-01" + ] +} diff --git a/test-rig/profiles/native119/packaged-optional-power.json b/test-rig/profiles/native119/packaged-optional-power.json new file mode 100644 index 00000000..188a1b62 --- /dev/null +++ b/test-rig/profiles/native119/packaged-optional-power.json @@ -0,0 +1,60 @@ +{ + "name": "packaged-optional-power", + "mode": "packaged", + "runtimeBase": "build/works-native119", + "minecraftVersion": "1.19.4", + "portStatus": "PORTED_UNQUALIFIED", + "qualificationStatus": "UNQUALIFIED", + "sourceCommit": "c9995315e2aee9d9bffec8eb8b1191ad8a0faeb7", + "sourceVersion": "2.4.11.119041", + "powerDeobfSha256": "512FDEC9275B33533EF773C110E6229E0F07268775FAD4B5942A15B68A9EB87D", + "levelType": "minecraft:flat", + "levelSeed": "advantage-works-1.19.4-v1", + "startupTimeoutSeconds": 180, + "forge": { + "version": "1.19.4-45.4.0", + "installerPath": "../../../../BaseMetals/.runtime-cache/forge-1.19.4-45.4.0-installer.jar", + "url": "https://maven.minecraftforge.net/net/minecraftforge/forge/1.19.4-45.4.0/forge-1.19.4-45.4.0-installer.jar", + "sha256": "6643BB8C21F409CC528FF656DAF60C206D304CC77BF529BBBAB4B336004DFC53" + }, + "mods": [ + { + "id": "basepower", + "version": "2.4.11.119041", + "file": "basePower-2.4.11.119041.jar", + "path": "../build/libs/basePower-2.4.11.119041.jar", + "sha256": "E1689C1147F6F6F93F6516D9188A279093D6CCC2FF1FBEB506468B876BA03E60" + }, + { + "id": "basesteam", + "version": "2.2.12.119041", + "file": "baseSteam-2.2.12.119041.jar", + "path": "../../SteamAdvantage/SteamAdvantage/build/libs/baseSteam-2.2.12.119041.jar", + "sha256": "13E29CDFCA22BCEE30E6F7260C5DC11524C405BDF5DB16731CD6328C06AC548D" + }, + { + "id": "baseelectric", + "version": "2.2.12.119041", + "file": "baseElectric-2.2.12.119041.jar", + "path": "../../ElectricAdvantage/ElectricAdvantage/build/libs/baseElectric-2.2.12.119041.jar", + "sha256": "0064D563F9E83E126B6E57D107F8BCEF5AD80276EF422BFBCDB702A0CAD38C1A" + }, + { + "id": "orespawn", + "version": "4.1.0.119041", + "file": "OreSpawn-4.1.0.119041.jar", + "path": "../../../../MinecraftMineralogy/OS119/OreSpawn/build/libs/OreSpawn-4.1.0.119041.jar", + "sha256": "1B2ECAA546E7FFB35863AC1D9FF92504471EE857045DCD01A068C5CA7F53CFFD" + }, + { + "id": "advantageworkstest", + "version": "1.1.3.119041", + "file": "AdvantageWorksTestHarness-1.1.3.119041.jar", + "path": "build/gradle/libs/AdvantageWorksTestHarness-1.1.3.119041.jar", + "sha256": "483E3E73568ADD9150084F6A2AFBAFCBB44666553B289066955D718D2CB0DA23" + } + ], + "lifecycleStations": [ + "P-API-01" + ] +} diff --git a/test-rig/profiles/native119/worldgen-advantage.json b/test-rig/profiles/native119/worldgen-advantage.json new file mode 100644 index 00000000..ad70f11e --- /dev/null +++ b/test-rig/profiles/native119/worldgen-advantage.json @@ -0,0 +1,153 @@ +{ + "mode": "packaged", + "runtimeBase": "build/works-native119-worldgen", + "serverPort": 25565, + "startupTimeoutSeconds": 180, + "stationRunSeconds": 5, + "recipeMode": "NORMAL", + "sourceCommit": "c9995315e2aee9d9bffec8eb8b1191ad8a0faeb7", + "sourceVersion": "2.4.11.119041", + "forge": { + "version": "1.19.4-45.4.0", + "installerPath": "../../../../BaseMetals/.runtime-cache/forge-1.19.4-45.4.0-installer.jar", + "url": "https://maven.minecraftforge.net/net/minecraftforge/forge/1.19.4-45.4.0/forge-1.19.4-45.4.0-installer.jar", + "sha256": "6643BB8C21F409CC528FF656DAF60C206D304CC77BF529BBBAB4B336004DFC53" + }, + "mods": [ + { + "id": "basepower", + "version": "2.4.11.119041", + "file": "basePower-2.4.11.119041.jar", + "path": "../build/libs/basePower-2.4.11.119041.jar", + "sha256": "E1689C1147F6F6F93F6516D9188A279093D6CCC2FF1FBEB506468B876BA03E60" + }, + { + "id": "basesteam", + "version": "2.2.12.119041", + "file": "baseSteam-2.2.12.119041.jar", + "path": "../../SteamAdvantage/SteamAdvantage/build/libs/baseSteam-2.2.12.119041.jar", + "sha256": "13E29CDFCA22BCEE30E6F7260C5DC11524C405BDF5DB16731CD6328C06AC548D" + }, + { + "id": "baseelectric", + "version": "2.2.12.119041", + "file": "baseElectric-2.2.12.119041.jar", + "path": "../../ElectricAdvantage/ElectricAdvantage/build/libs/baseElectric-2.2.12.119041.jar", + "sha256": "0064D563F9E83E126B6E57D107F8BCEF5AD80276EF422BFBCDB702A0CAD38C1A" + }, + { + "id": "orespawn", + "version": "4.1.0.119041", + "file": "OreSpawn-4.1.0.119041.jar", + "path": "../../../../MinecraftMineralogy/OS119/OreSpawn/build/libs/OreSpawn-4.1.0.119041.jar", + "sha256": "1B2ECAA546E7FFB35863AC1D9FF92504471EE857045DCD01A068C5CA7F53CFFD" + }, + { + "id": "advantageworkstest", + "version": "1.1.3.119041", + "file": "AdvantageWorksTestHarness-1.1.3.119041.jar", + "path": "build/gradle/libs/AdvantageWorksTestHarness-1.1.3.119041.jar", + "sha256": "483E3E73568ADD9150084F6A2AFBAFCBB44666553B289066955D718D2CB0DA23" + } + ], + "name": "worldgen-advantage", + "levelName": "AdvantageWorksWorldgen", + "levelSeed": "advantage-works-worldgen-1.15.2-v1", + "levelType": "minecraft:normal", + "generatorSettings": "", + "buildWorks": false, + "worldgenSampleRadius": 8, + "shutdownTimeoutSeconds": 300, + "lifecycleStations": [], + "firstProfileAssertions": [ + { + "id": "desert-oil-visible", + "section": "fluid_deposits", + "entry": "basepower:fluid_deposit/desert_crude_oil", + "present": true, + "field": "enabled", + "expectedBoolean": true + }, + { + "id": "desert-oil-owner", + "section": "fluid_deposits", + "entry": "basepower:fluid_deposit/desert_crude_oil", + "present": true, + "field": "source_provider", + "expectedString": "basepower" + }, + { + "id": "electric-sulfur-default", + "section": "ores", + "entry": "baseelectric:ore/sulfur", + "present": true, + "field": "enabled", + "expectedBoolean": true + }, + { + "id": "electric-lithium-default", + "section": "ores", + "entry": "baseelectric:ore/lithium", + "present": true, + "field": "enabled", + "expectedBoolean": true + } + ], + "profileMutations": [ + { + "section": "fluid_deposits", + "entry": "basepower:fluid_deposit/desert_crude_oil", + "field": "enabled", + "booleanValue": false + }, + { + "section": "ores", + "entry": "baseelectric:ore/sulfur", + "field": "enabled", + "booleanValue": false + } + ], + "restartProfileAssertions": [ + { + "id": "desert-oil-user-disable-persists", + "section": "fluid_deposits", + "entry": "basepower:fluid_deposit/desert_crude_oil", + "present": true, + "field": "enabled", + "expectedBoolean": false + }, + { + "id": "electric-sulfur-user-disable-persists", + "section": "ores", + "entry": "baseelectric:ore/sulfur", + "present": true, + "field": "enabled", + "expectedBoolean": false + }, + { + "id": "electric-lithium-remains-enabled", + "section": "ores", + "entry": "baseelectric:ore/lithium", + "present": true, + "field": "enabled", + "expectedBoolean": true + } + ], + "worldgenExpectations": { + "minimumCounts": { + "basepower:crude_oil": 1, + "baseelectric:li_ore": 1, + "baseelectric:sulfur_ore": 1 + }, + "maximumCounts": { + "mineralogy:crude_oil": 0, + "mineralogy:sulfur_ore": 0 + }, + "zeroBiomeViolations": true + }, + "minecraftVersion": "1.19.4", + "portStatus": "PORTED_UNQUALIFIED", + "qualificationStatus": "UNQUALIFIED", + "inputStatus": "ARTIFACTS_AND_SOURCES_PINNED_QUALIFICATION_PENDING", + "powerDeobfSha256": "512FDEC9275B33533EF773C110E6229E0F07268775FAD4B5942A15B68A9EB87D" +} diff --git a/test-rig/profiles/native119/worldgen-legacy-import.json b/test-rig/profiles/native119/worldgen-legacy-import.json new file mode 100644 index 00000000..e04e98e9 --- /dev/null +++ b/test-rig/profiles/native119/worldgen-legacy-import.json @@ -0,0 +1,220 @@ +{ + "mode": "packaged", + "runtimeBase": "build/works-native119-worldgen", + "serverPort": 25565, + "startupTimeoutSeconds": 180, + "stationRunSeconds": 5, + "recipeMode": "NORMAL", + "sourceCommit": "c9995315e2aee9d9bffec8eb8b1191ad8a0faeb7", + "sourceVersion": "2.4.11.119041", + "forge": { + "version": "1.19.4-45.4.0", + "installerPath": "../../../../BaseMetals/.runtime-cache/forge-1.19.4-45.4.0-installer.jar", + "url": "https://maven.minecraftforge.net/net/minecraftforge/forge/1.19.4-45.4.0/forge-1.19.4-45.4.0-installer.jar", + "sha256": "6643BB8C21F409CC528FF656DAF60C206D304CC77BF529BBBAB4B336004DFC53" + }, + "mods": [ + { + "id": "basepower", + "version": "2.4.11.119041", + "file": "basePower-2.4.11.119041.jar", + "path": "../build/libs/basePower-2.4.11.119041.jar", + "sha256": "E1689C1147F6F6F93F6516D9188A279093D6CCC2FF1FBEB506468B876BA03E60" + }, + { + "id": "basesteam", + "version": "2.2.12.119041", + "file": "baseSteam-2.2.12.119041.jar", + "path": "../../SteamAdvantage/SteamAdvantage/build/libs/baseSteam-2.2.12.119041.jar", + "sha256": "13E29CDFCA22BCEE30E6F7260C5DC11524C405BDF5DB16731CD6328C06AC548D" + }, + { + "id": "baseelectric", + "version": "2.2.12.119041", + "file": "baseElectric-2.2.12.119041.jar", + "path": "../../ElectricAdvantage/ElectricAdvantage/build/libs/baseElectric-2.2.12.119041.jar", + "sha256": "0064D563F9E83E126B6E57D107F8BCEF5AD80276EF422BFBCDB702A0CAD38C1A" + }, + { + "id": "orespawn", + "version": "4.1.0.119041", + "file": "OreSpawn-4.1.0.119041.jar", + "path": "../../../../MinecraftMineralogy/OS119/OreSpawn/build/libs/OreSpawn-4.1.0.119041.jar", + "sha256": "1B2ECAA546E7FFB35863AC1D9FF92504471EE857045DCD01A068C5CA7F53CFFD" + }, + { + "id": "advantageworkstest", + "version": "1.1.3.119041", + "file": "AdvantageWorksTestHarness-1.1.3.119041.jar", + "path": "build/gradle/libs/AdvantageWorksTestHarness-1.1.3.119041.jar", + "sha256": "483E3E73568ADD9150084F6A2AFBAFCBB44666553B289066955D718D2CB0DA23" + } + ], + "name": "worldgen-legacy-import", + "levelName": "AdvantageWorksWorldgen", + "levelSeed": "advantage-works-worldgen-1.15.2-v1", + "levelType": "minecraft:normal", + "generatorSettings": "", + "buildWorks": false, + "worldgenSampleRadius": 8, + "stableWorldgenCountExclusions": [ + "basepower:crude_oil" + ], + "shutdownTimeoutSeconds": 300, + "lifecycleStations": [], + "configFixtures": [ + { + "source": "../../../PA113/PowerAdvantage/test-rig/../src/test/resources/orespawn-legacy/poweradvantage.json", + "destination": "config/orespawn/poweradvantage.json" + }, + { + "source": "../../../PA113/PowerAdvantage/test-rig/../../ElectricAdvantage/ElectricAdvantage/src/test/resources/orespawn-legacy/electricadvantage.json", + "destination": "config/orespawn/electricadvantage.json" + } + ], + "firstProfileAssertions": [ + { + "id": "legacy-oil-retained", + "section": "ores", + "entry": "basepower:legacy/basepower_crude_oil_0", + "present": true, + "field": "enabled", + "expectedBoolean": true + }, + { + "id": "legacy-lithium-retained", + "section": "ores", + "entry": "baseelectric:legacy/baseelectric_li_ore_0", + "present": true, + "field": "enabled", + "expectedBoolean": true + }, + { + "id": "legacy-sulfur-retained", + "section": "ores", + "entry": "baseelectric:legacy/baseelectric_sulfur_ore_1", + "present": true, + "field": "enabled", + "expectedBoolean": true + }, + { + "id": "modern-oil-not-duplicated", + "section": "fluid_deposits", + "entry": "basepower:fluid_deposit/desert_crude_oil", + "present": false + }, + { + "id": "modern-lithium-not-duplicated", + "section": "ores", + "entry": "baseelectric:ore/lithium", + "present": false + }, + { + "id": "modern-sulfur-not-duplicated", + "section": "ores", + "entry": "baseelectric:ore/sulfur", + "present": false + } + ], + "restartProfileAssertions": [ + { + "id": "legacy-oil-retained", + "section": "ores", + "entry": "basepower:legacy/basepower_crude_oil_0", + "present": true, + "field": "enabled", + "expectedBoolean": true + }, + { + "id": "legacy-lithium-retained", + "section": "ores", + "entry": "baseelectric:legacy/baseelectric_li_ore_0", + "present": true, + "field": "enabled", + "expectedBoolean": true + }, + { + "id": "legacy-sulfur-retained", + "section": "ores", + "entry": "baseelectric:legacy/baseelectric_sulfur_ore_1", + "present": true, + "field": "enabled", + "expectedBoolean": true + }, + { + "id": "modern-oil-not-duplicated", + "section": "fluid_deposits", + "entry": "basepower:fluid_deposit/desert_crude_oil", + "present": false + }, + { + "id": "modern-lithium-not-duplicated", + "section": "ores", + "entry": "baseelectric:ore/lithium", + "present": false + }, + { + "id": "modern-sulfur-not-duplicated", + "section": "ores", + "entry": "baseelectric:ore/sulfur", + "present": false + } + ], + "firstFileAssertions": [ + { + "id": "migration-report", + "path": "config/orespawn-os3-migration-report.json", + "contains": "os1_config_source", + "minimumOccurrences": 2 + }, + { + "id": "power-provider", + "path": "config/basepower-orespawn.json", + "contains": "basepower:legacy/basepower_crude_oil_0", + "minimumOccurrences": 1 + }, + { + "id": "electric-provider", + "path": "config/baseelectric-orespawn.json", + "contains": "baseelectric:legacy/baseelectric_li_ore_0", + "minimumOccurrences": 1 + } + ], + "restartFileAssertions": [ + { + "id": "migration-report", + "path": "config/orespawn-os3-migration-report.json", + "contains": "os1_config_source", + "minimumOccurrences": 2 + }, + { + "id": "power-provider", + "path": "config/basepower-orespawn.json", + "contains": "basepower:legacy/basepower_crude_oil_0", + "minimumOccurrences": 1 + }, + { + "id": "electric-provider", + "path": "config/baseelectric-orespawn.json", + "contains": "baseelectric:legacy/baseelectric_li_ore_0", + "minimumOccurrences": 1 + } + ], + "worldgenExpectations": { + "minimumCounts": { + "basepower:crude_oil": 1, + "baseelectric:li_ore": 1, + "baseelectric:sulfur_ore": 1 + }, + "maximumCounts": { + "mineralogy:crude_oil": 0, + "mineralogy:sulfur_ore": 0 + }, + "zeroBiomeViolations": false + }, + "minecraftVersion": "1.19.4", + "portStatus": "PORTED_UNQUALIFIED", + "qualificationStatus": "UNQUALIFIED", + "inputStatus": "ARTIFACTS_AND_SOURCES_PINNED_QUALIFICATION_PENDING", + "powerDeobfSha256": "512FDEC9275B33533EF773C110E6229E0F07268775FAD4B5942A15B68A9EB87D" +} diff --git a/test-rig/profiles/native119/worldgen-mineralogy.json b/test-rig/profiles/native119/worldgen-mineralogy.json new file mode 100644 index 00000000..efa0e29b --- /dev/null +++ b/test-rig/profiles/native119/worldgen-mineralogy.json @@ -0,0 +1,163 @@ +{ + "mode": "packaged", + "runtimeBase": "build/works-native119-worldgen", + "serverPort": 25565, + "startupTimeoutSeconds": 180, + "stationRunSeconds": 5, + "recipeMode": "NORMAL", + "sourceCommit": "c9995315e2aee9d9bffec8eb8b1191ad8a0faeb7", + "sourceVersion": "2.4.11.119041", + "forge": { + "version": "1.19.4-45.4.0", + "installerPath": "../../../../BaseMetals/.runtime-cache/forge-1.19.4-45.4.0-installer.jar", + "url": "https://maven.minecraftforge.net/net/minecraftforge/forge/1.19.4-45.4.0/forge-1.19.4-45.4.0-installer.jar", + "sha256": "6643BB8C21F409CC528FF656DAF60C206D304CC77BF529BBBAB4B336004DFC53" + }, + "mods": [ + { + "id": "basepower", + "version": "2.4.11.119041", + "file": "basePower-2.4.11.119041.jar", + "path": "../build/libs/basePower-2.4.11.119041.jar", + "sha256": "E1689C1147F6F6F93F6516D9188A279093D6CCC2FF1FBEB506468B876BA03E60" + }, + { + "id": "basesteam", + "version": "2.2.12.119041", + "file": "baseSteam-2.2.12.119041.jar", + "path": "../../SteamAdvantage/SteamAdvantage/build/libs/baseSteam-2.2.12.119041.jar", + "sha256": "13E29CDFCA22BCEE30E6F7260C5DC11524C405BDF5DB16731CD6328C06AC548D" + }, + { + "id": "baseelectric", + "version": "2.2.12.119041", + "file": "baseElectric-2.2.12.119041.jar", + "path": "../../ElectricAdvantage/ElectricAdvantage/build/libs/baseElectric-2.2.12.119041.jar", + "sha256": "0064D563F9E83E126B6E57D107F8BCEF5AD80276EF422BFBCDB702A0CAD38C1A" + }, + { + "id": "orespawn", + "version": "4.1.0.119041", + "file": "OreSpawn-4.1.0.119041.jar", + "path": "../../../../MinecraftMineralogy/OS119/OreSpawn/build/libs/OreSpawn-4.1.0.119041.jar", + "sha256": "1B2ECAA546E7FFB35863AC1D9FF92504471EE857045DCD01A068C5CA7F53CFFD" + }, + { + "id": "advantageworkstest", + "version": "1.1.3.119041", + "file": "AdvantageWorksTestHarness-1.1.3.119041.jar", + "path": "build/gradle/libs/AdvantageWorksTestHarness-1.1.3.119041.jar", + "sha256": "483E3E73568ADD9150084F6A2AFBAFCBB44666553B289066955D718D2CB0DA23" + }, + { + "id": "mineralogy", + "version": "6.1.4.119041", + "sourceRef": "84222a0f129c086fd5ddcdb4244468f50ac0d4d1", + "file": "Mineralogy-6.1.4.119041.jar", + "path": "../../../../MinecraftMineralogy/MinecraftMineralogy 119/MinecraftMineralogy/build/libs/Mineralogy-6.1.4.119041.jar", + "sha256": "0E197D92B74F8466F35B31357E12629649EA58FF655CEFDF0D1991BE72DDA8A6" + } + ], + "name": "worldgen-mineralogy", + "levelName": "AdvantageWorksWorldgen", + "levelSeed": "advantage-works-worldgen-1.15.2-v1", + "levelType": "minecraft:normal", + "generatorSettings": "", + "buildWorks": false, + "worldgenSampleRadius": 16, + "shutdownTimeoutSeconds": 300, + "lifecycleStations": [], + "firstProfileAssertions": [ + { + "id": "desert-oil-enabled", + "section": "fluid_deposits", + "entry": "basepower:fluid_deposit/desert_crude_oil", + "present": true, + "field": "enabled", + "expectedBoolean": true + }, + { + "id": "ocean-oil-enabled", + "section": "fluid_deposits", + "entry": "mineralogy:fluid_deposit/crude_oil", + "present": true, + "field": "enabled", + "expectedBoolean": true + }, + { + "id": "mineralogy-sulfur-enabled", + "section": "ores", + "entry": "mineralogy:ore/mineralogy/sulfur_ore", + "present": true, + "field": "enabled", + "expectedBoolean": true + }, + { + "id": "electric-sulfur-disabled", + "section": "ores", + "entry": "baseelectric:ore/sulfur", + "present": true, + "field": "enabled", + "expectedBoolean": false + }, + { + "id": "electric-lithium-fallback-enabled", + "section": "ores", + "entry": "baseelectric:ore/lithium", + "present": true, + "field": "enabled", + "expectedBoolean": true + } + ], + "profileMutations": [ + { + "section": "ores", + "entry": "baseelectric:ore/sulfur", + "field": "enabled", + "booleanValue": true + } + ], + "restartProfileAssertions": [ + { + "id": "electric-sulfur-user-enable-persists", + "section": "ores", + "entry": "baseelectric:ore/sulfur", + "present": true, + "field": "enabled", + "expectedBoolean": true + }, + { + "id": "mineralogy-sulfur-remains-enabled", + "section": "ores", + "entry": "mineralogy:ore/mineralogy/sulfur_ore", + "present": true, + "field": "enabled", + "expectedBoolean": true + }, + { + "id": "ocean-oil-remains-enabled", + "section": "fluid_deposits", + "entry": "mineralogy:fluid_deposit/crude_oil", + "present": true, + "field": "enabled", + "expectedBoolean": true + } + ], + "worldgenExpectations": { + "minimumCounts": { + "mineralogy:crude_oil": 1, + "mineralogy:sulfur_ore": 1, + "basepower:crude_oil": 1, + "baseelectric:li_ore": 1 + }, + "maximumCounts": { + "baseelectric:sulfur_ore": 0 + }, + "zeroBiomeViolations": true + }, + "minecraftVersion": "1.19.4", + "portStatus": "PORTED_UNQUALIFIED", + "qualificationStatus": "UNQUALIFIED", + "inputStatus": "ARTIFACTS_AND_SOURCES_PINNED_QUALIFICATION_PENDING", + "powerDeobfSha256": "512FDEC9275B33533EF773C110E6229E0F07268775FAD4B5942A15B68A9EB87D" +} diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/ActorProbe.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/ActorProbe.java index 598288dd..577005e8 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/ActorProbe.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/ActorProbe.java @@ -45,23 +45,23 @@ private boolean frozen(net.minecraft.world.level.Level world) { static boolean witnessed(Entity entity) { return NativeEntityData.containsString(entity, "AdvWorksActor"); } - private void tick(TickEvent.WorldTickEvent event) { - if (!(event.world instanceof ServerLevel)) return; - if (event.phase == TickEvent.Phase.START && frozen(event.world)) { - NativeEntityHold held = heldActors.computeIfAbsent(event.world, key -> new NativeEntityHold()); - Native117Worlds.entities((ServerLevel) event.world).filter(ActorProbe::witnessed) + private void tick(TickEvent.LevelTickEvent event) { + if (!(event.level instanceof ServerLevel)) return; + if (event.phase == TickEvent.Phase.START && frozen(event.level)) { + NativeEntityHold held = heldActors.computeIfAbsent(event.level, key -> new NativeEntityHold()); + Native117Worlds.entities((ServerLevel) event.level).filter(ActorProbe::witnessed) .forEach(held::hold); - heldActors.put(event.world, held); + heldActors.put(event.level, held); } else if (event.phase == TickEvent.Phase.END) { - NativeEntityHold held = heldActors.remove(event.world); + NativeEntityHold held = heldActors.remove(event.level); if (held != null) held.restore(); } } - private void entityJoining(net.minecraftforge.event.entity.EntityJoinWorldEvent event) { - if (event.getWorld() instanceof ServerLevel && frozen(event.getWorld()) + private void entityJoining(net.minecraftforge.event.entity.EntityJoinLevelEvent event) { + if (event.getLevel() instanceof ServerLevel && frozen(event.getLevel()) && witnessed(event.getEntity())) { - heldActors.computeIfAbsent(event.getWorld(), key -> new NativeEntityHold()).hold(event.getEntity()); + heldActors.computeIfAbsent(event.getLevel(), key -> new NativeEntityHold()).hold(event.getEntity()); } } diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/AdvantageWorksCommand.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/AdvantageWorksCommand.java index e0ccb101..8f6c1958 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/AdvantageWorksCommand.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/AdvantageWorksCommand.java @@ -6,7 +6,7 @@ import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; -import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.Component; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -61,7 +61,7 @@ private static int execute(CommandSourceStack source, String operation, String t case "checkpoint": message = controller.checkpoint(source.getServer(), target, name); break; default: throw new IllegalArgumentException("Unknown Advantage Works operation " + operation); } - source.sendSuccess(new TextComponent(message), false); + source.sendSuccess(Component.literal(message), false); return 1; } catch (RuntimeException ex) { LOGGER.error("Advantage Works command failed: {} {}", operation, target, ex); @@ -72,7 +72,7 @@ private static int execute(CommandSourceStack source, String operation, String t private static int sampleWorldgen(CommandSourceStack source, int radius) { WorksController controller = WorksController.getInstance(); controller.requireAuthorizedWorld(source.getServer()); - source.sendSuccess(new TextComponent(controller.sampleWorldgen(source.getServer(), radius)), false); + source.sendSuccess(Component.literal(controller.sampleWorldgen(source.getServer(), radius)), false); return 1; } } diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/AdvantageWorksMod.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/AdvantageWorksMod.java index 1b0b13c2..b148ff6f 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/AdvantageWorksMod.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/AdvantageWorksMod.java @@ -3,17 +3,13 @@ import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.event.RegisterCommandsEvent; -import net.minecraftforge.common.world.ForgeWorldPreset; -import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; @Mod(AdvantageWorksMod.MODID) public final class AdvantageWorksMod { public static final String MODID = "advantageworkstest"; - public static final String VERSION = "1.1.2.118021"; + public static final String VERSION = "1.1.3.119041"; public AdvantageWorksMod() { - FMLJavaModLoadingContext.get().getModEventBus() - .addGenericListener(ForgeWorldPreset.class, Native118TestFlat::register); MigrationProbe.register(); ConnectedProbe.register(); ActorProbe.register(); diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/ConnectedProbe.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/ConnectedProbe.java index d46fe3b5..a5b4734e 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/ConnectedProbe.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/ConnectedProbe.java @@ -43,17 +43,17 @@ private ConnectedSession get(ServerLevel current) throws Exception { return session; } - private void tick(TickEvent.WorldTickEvent event) { - if (event.world.isClientSide || Native117Worlds.legacyId(event.world.dimension()) != 0) return; + private void tick(TickEvent.LevelTickEvent event) { + if (event.level.isClientSide || Native117Worlds.legacyId(event.level.dimension()) != 0) return; try { - ConnectedSession current = get((ServerLevel) event.world); + ConnectedSession current = get((ServerLevel) event.level); if (event.phase == TickEvent.Phase.START) current.start(); else current.end(); } catch (Exception ex) { throw new IllegalStateException("Connected probe failed", ex); } } - private void chunkLoaded(net.minecraftforge.event.world.ChunkEvent.Load event) { - if (!(event.getWorld() instanceof ServerLevel)) return; - ServerLevel loaded = (ServerLevel) event.getWorld(); + private void chunkLoaded(net.minecraftforge.event.level.ChunkEvent.Load event) { + if (!(event.getLevel() instanceof ServerLevel)) return; + ServerLevel loaded = (ServerLevel) event.getLevel(); if (loaded.dimension() != net.minecraft.world.level.Level.OVERWORLD) return; if (session == null) held.addAll(NativeBlockEntityTicks.drain(loaded)); else if (world == loaded) session.start(); @@ -101,7 +101,7 @@ private void execute(ServerLevel current, String operation, String label, int ti entry.remove("seed"); BlockPos pos = new BlockPos(e.get("x").getAsInt(), e.get("y").getAsInt(), e.get("z").getAsInt()); BlockState state = world.getBlockState(pos); - entry.addProperty("actualName", state.getBlock().getRegistryName().toString()); + entry.addProperty("actualName", net.minecraftforge.registries.ForgeRegistries.BLOCKS.getKey(state.getBlock()).toString()); JsonObject properties = new JsonObject(); for (Property p : state.getProperties()) properties.addProperty(p.getName(), state.getValue(p).toString().toLowerCase(java.util.Locale.ROOT)); entry.add("properties", properties); diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/DimensionEntityProbe.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/DimensionEntityProbe.java index 8062597e..8731e41a 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/DimensionEntityProbe.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/DimensionEntityProbe.java @@ -48,7 +48,7 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayerGameMode; import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.network.PacketSendListener; import net.minecraft.core.Direction; import net.minecraft.core.BlockPos; import net.minecraft.world.level.Level; @@ -56,7 +56,7 @@ import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.util.FakePlayer; import net.minecraftforge.common.util.FakePlayerFactory; -import net.minecraftforge.event.world.WorldEvent; +import net.minecraftforge.event.level.LevelEvent; import net.minecraftforge.event.TickEvent; import net.minecraftforge.event.RegisterCommandsEvent; @@ -98,9 +98,9 @@ private boolean frozen(Level world, JsonObject definition) { private static boolean witnessed(Entity entity) { return NativeEntityData.containsString(entity, WITNESS); } - private void load(WorldEvent.Load event) { - if (!(event.getWorld() instanceof ServerLevel)) return; - ServerLevel world = (ServerLevel) event.getWorld(); + private void load(LevelEvent.Load event) { + if (!(event.getLevel() instanceof ServerLevel)) return; + ServerLevel world = (ServerLevel) event.getLevel(); if (!marked(world) || !DEFINITION.isFile()) return; try { JsonObject definition = definition(); @@ -108,37 +108,37 @@ private void load(WorldEvent.Load event) { } catch (Exception ex) { throw new IllegalStateException("Dimension load preparation failed", ex); } } - private void tick(TickEvent.WorldTickEvent event) { - if (!marked(event.world) || !DEFINITION.isFile()) return; + private void tick(TickEvent.LevelTickEvent event) { + if (!marked(event.level) || !DEFINITION.isFile()) return; if (event.phase == TickEvent.Phase.START) { try { JsonObject definition = definition(); int selected = dimension(definition); - if (event.world.dimension().equals(Level.OVERWORLD) && nativeOnLoad(definition)) { - ServerLevel selectedWorld = event.world.getServer().getLevel(Native117Worlds.key(selected)); + if (event.level.dimension().equals(Level.OVERWORLD) && nativeOnLoad(definition)) { + ServerLevel selectedWorld = event.level.getServer().getLevel(Native117Worlds.key(selected)); if (selectedWorld == null || !marked(selectedWorld)) throw new IllegalStateException("Dimension unavailable"); prepare(selectedWorld); } - if (Native117Worlds.legacyId(event.world.dimension()) != selected) return; + if (Native117Worlds.legacyId(event.level.dimension()) != selected) return; // Pin/load before either frozen observation or normal native initialization. - prepare((ServerLevel) event.world); - if (!frozen(event.world, definition)) return; - NativeEntityHold hold = held.computeIfAbsent(event.world, key -> new NativeEntityHold()); - for (Entity entity : witnesses((ServerLevel) event.world)) hold.hold(entity); - held.put(event.world, hold); + prepare((ServerLevel) event.level); + if (!frozen(event.level, definition)) return; + NativeEntityHold hold = held.computeIfAbsent(event.level, key -> new NativeEntityHold()); + for (Entity entity : witnesses((ServerLevel) event.level)) hold.hold(entity); + held.put(event.level, hold); } catch (Exception ex) { throw new IllegalStateException("Dimension tick preparation failed", ex); } } else if (event.phase == TickEvent.Phase.END) { - NativeEntityHold hold = held.remove(event.world); + NativeEntityHold hold = held.remove(event.level); if (hold != null) hold.restore(); } } - private void entityJoining(net.minecraftforge.event.entity.EntityJoinWorldEvent event) { - if (!(event.getWorld() instanceof ServerLevel) || !marked(event.getWorld()) + private void entityJoining(net.minecraftforge.event.entity.EntityJoinLevelEvent event) { + if (!(event.getLevel() instanceof ServerLevel) || !marked(event.getLevel()) || !DEFINITION.isFile() || !witnessed(event.getEntity())) return; try { - if (frozen(event.getWorld(), definition())) { - held.computeIfAbsent(event.getWorld(), key -> new NativeEntityHold()).hold(event.getEntity()); + if (frozen(event.getLevel(), definition())) { + held.computeIfAbsent(event.getLevel(), key -> new NativeEntityHold()).hold(event.getEntity()); } } catch (Exception error) { throw new IllegalStateException("Dimension join hold failed", error); } } @@ -335,7 +335,7 @@ private JsonObject snapshot(MinecraftServer server, ServerLevel world, JsonObjec entry.add("passengerUuids", passengers); Entity leash = entity instanceof Mob ? ((Mob) entity).getLeashHolder() : null; entry.add("leashHolderUuid", leash == null ? JsonNull.INSTANCE : JSON.toJsonTree(leash.getUUID().toString())); - entry.add("leashHolderType", leash == null ? JsonNull.INSTANCE : JSON.toJsonTree(leash.getType().getRegistryName().toString())); + entry.add("leashHolderType", leash == null ? JsonNull.INSTANCE : JSON.toJsonTree(net.minecraftforge.registries.ForgeRegistries.ENTITY_TYPES.getKey(leash.getType()).toString())); BlockPos leashPos = leash instanceof HangingEntity ? ((HangingEntity) leash).getPos() : null; entry.add("leashHolderPosition", leashPos == null ? JsonNull.INSTANCE : JSON.toJsonTree(new int[] {leashPos.getX(), leashPos.getY(), leashPos.getZ()})); @@ -343,7 +343,7 @@ private JsonObject snapshot(MinecraftServer server, ServerLevel world, JsonObjec } result.add("entities", entities); BlockPos pos = new BlockPos(176, 101, 8); JsonObject generator = new JsonObject(); - generator.addProperty("actualName", world.getBlockState(pos).getBlock().getRegistryName().toString()); + generator.addProperty("actualName", net.minecraftforge.registries.ForgeRegistries.BLOCKS.getKey(world.getBlockState(pos).getBlock()).toString()); BlockEntity tile = world.getBlockEntity(pos); if (tile != null) generator.add("tile", encode(tile.saveWithFullMetadata())); result.add("generator", generator); return result; @@ -387,7 +387,7 @@ private static JsonObject respawn(MinecraftServer server, ServerLevel world, Jso world.getGameRules().getRule(net.minecraft.world.level.GameRules.RULE_KEEPINVENTORY).set(keep, server); destination.getGameRules().getRule(net.minecraft.world.level.GameRules.RULE_KEEPINVENTORY).set(keep, server); OfflineConnection connection = new OfflineConnection(server, player); - player.setHealth(0); player.die(DamageSource.GENERIC); + player.setHealth(0); player.die(player.damageSources().generic()); CompoundTag afterDeath = player.saveWithoutId(new CompoundTag()); result.add("afterDeath", encode(afterDeath)); diagnostics.add("afterDeath", spawnDiagnostics(player, afterDeath)); @@ -440,7 +440,7 @@ private static JsonObject spawnDiagnostics(ServerPlayer player, CompoundTag nbt) private static final class OfflineConnection extends ServerGamePacketListenerImpl { OfflineConnection(MinecraftServer server, ServerPlayer player) { super(server, new Connection(PacketFlow.SERVERBOUND), player); } @Override public void send(Packet packet) { /* Deliberately no socket or client login. */ } - @Override public void send(Packet packet, GenericFutureListener> listener) { + @Override public void send(Packet packet, PacketSendListener listener) { // Death-message packets use this overload; no native network work is queued. } } @@ -448,7 +448,7 @@ private static final class OfflineConnection extends ServerGamePacketListenerImp private static CompoundTag entityNbt(Entity entity) { if (entity instanceof LeashFenceKnotEntity) { CompoundTag nbt = entity.saveWithoutId(new CompoundTag()); - nbt.putString("id", entity.getType().getRegistryName().toString()); + nbt.putString("id", net.minecraftforge.registries.ForgeRegistries.ENTITY_TYPES.getKey(entity.getType()).toString()); return nbt; } CompoundTag nbt = new CompoundTag(); diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/ElectricOperationalCases.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/ElectricOperationalCases.java index 67c466b8..0e7e9a45 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/ElectricOperationalCases.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/ElectricOperationalCases.java @@ -19,7 +19,6 @@ import net.minecraft.core.BlockPos; import net.minecraft.world.level.GameRules; import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.common.util.FakePlayer; @@ -231,7 +230,7 @@ private static void still(List cases) { for (Direction face : Direction.values()) { FluidStack simulated = fluids(c, 8, face).drain(24, IFluidHandler.FluidAction.SIMULATE); equal(c, "face." + face + ".drainable", simulated.getAmount(), 24); - c.require(new ResourceLocation("basepower:refined_oil").equals(simulated.getFluid().getRegistryName()), "Face offers the real refined product " + face); + c.require(new ResourceLocation("basepower:refined_oil").equals(net.minecraftforge.registries.ForgeRegistries.FLUIDS.getKey(simulated.getFluid())), "Face offers the real refined product " + face); equal(c, "face." + face + ".drainSimulationUnchanged", tankAmount(c, 8, 1), 24); } })); @@ -408,7 +407,7 @@ private static void hydro(List cases) { int turbines = 0; for (Entity actor : c.world.getEntitiesOfClass(Entity.class, new AABB(c.at(0, 0, 0), c.at(31, 31, 31)))) { - if (!new ResourceLocation(EA + "hydroturbine").equals(actor.getType().getRegistryName())) continue; + if (!new ResourceLocation(EA + "hydroturbine").equals(net.minecraftforge.registries.ForgeRegistries.ENTITY_TYPES.getKey(actor.getType()))) continue; turbines++; c.record("hydro.motion/" + actor.getUUID(), actor.getDeltaMovement().toString()); c.require(actor.getDeltaMovement().lengthSqr() == 0, @@ -726,7 +725,7 @@ private static int output(MachineContext c, int x, int first, int slots, String } private static int stackCount(ItemStack stack, String id) { - return stack == null || stack.isEmpty() || !new ResourceLocation(id).equals(stack.getItem().getRegistryName()) ? 0 : stack.getCount(); + return stack == null || stack.isEmpty() || !new ResourceLocation(id).equals(net.minecraftforge.registries.ForgeRegistries.ITEMS.getKey(stack.getItem())) ? 0 : stack.getCount(); } private static IFluidHandler fluids(MachineContext c, int x) { @@ -734,7 +733,7 @@ private static IFluidHandler fluids(MachineContext c, int x) { } private static IFluidHandler fluids(MachineContext c, int x, Direction face) { - return c.tile(x, Y, Z).getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, face) + return c.tile(x, Y, Z).getCapability(net.minecraftforge.common.capabilities.ForgeCapabilities.FLUID_HANDLER, face) .orElseThrow(() -> new IllegalStateException("Missing real fluid capability at " + x)); } @@ -762,8 +761,8 @@ private static int tankAmount(MachineContext c, int x, int tank) { private static void fluidIs(MachineContext c, int x, int tank, String id) { FluidStack fluid = fluids(c, x).getFluidInTank(tank); - c.record("fluid." + x + "." + tank, String.valueOf(fluid.getFluid().getRegistryName())); - c.require(new ResourceLocation(id).equals(fluid.getFluid().getRegistryName()), "Tank contains " + id); + c.record("fluid." + x + "." + tank, String.valueOf(net.minecraftforge.registries.ForgeRegistries.FLUIDS.getKey(fluid.getFluid()))); + c.require(new ResourceLocation(id).equals(net.minecraftforge.registries.ForgeRegistries.FLUIDS.getKey(fluid.getFluid())), "Tank contains " + id); } private static void rejectFluid(MachineContext c, int x, String id, int amount) { diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/LifecycleProbe.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/LifecycleProbe.java index 3dc54b50..4bf175e1 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/LifecycleProbe.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/LifecycleProbe.java @@ -27,7 +27,7 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.event.world.ChunkEvent; +import net.minecraftforge.event.level.ChunkEvent; import net.minecraftforge.event.TickEvent; import net.minecraftforge.fml.ModList; import net.minecraftforge.event.RegisterCommandsEvent; @@ -240,7 +240,7 @@ private void execute(ServerLevel world, String operation, String label) { String expected = oil.bucket.substring(0, oil.bucket.length() - "_bucket".length()); net.minecraft.world.level.material.FluidState state = world.getFluidState(source); if (!state.isSource() || !expected.equals( - net.minecraft.core.Registry.FLUID.getKey(state.getType()).toString())) + net.minecraft.core.registries.BuiltInRegistries.FLUID.getKey(state.getType()).toString())) throw new IllegalStateException("Bucket placed the wrong fluid: " + oil.id); } break; @@ -250,7 +250,7 @@ private void execute(ServerLevel world, String operation, String label) { if (fixture.from == null) continue; int[] current = fixture.route == null ? fixture.from : fixture.route.get(routeSteps.getOrDefault(fixture.id, 0)); BlockEntity tile = world.getBlockEntity(position(current)); - if (!(tile instanceof PoweredEntity) || !fixture.block.equals(world.getBlockState(tile.getBlockPos()).getBlock().getRegistryName().toString())) + if (!(tile instanceof PoweredEntity) || !fixture.block.equals(net.minecraftforge.registries.ForgeRegistries.BLOCKS.getKey(world.getBlockState(tile.getBlockPos()).getBlock()).toString())) throw new IllegalStateException("Missing drill " + fixture.id); float reserve = "low-prime".equals(operation) ? ("steam".equals(fixture.energyType) ? 9 : 249) : fixture.initialEnergy; ((PoweredEntity) tile).setEnergy(reserve, new ConduitType(fixture.energyType)); @@ -349,7 +349,7 @@ private boolean roundTripPrerequisites(RoundTripRequest request) { flags.put("notRemoved", tile != null && !tile.isRemoved()); flags.put("blockTypeValid", tile instanceof PoweredEntity && chunk != null && tile.getType().isValid(chunk.getBlockState(pos)) - && fixture.block.equals(chunk.getBlockState(pos).getBlock().getRegistryName().toString())); + && fixture.block.equals(net.minecraftforge.registries.ForgeRegistries.BLOCKS.getKey(chunk.getBlockState(pos).getBlock()).toString())); String bindingFailure = null; if (tile != null) { try { request.bindings.put(fixture.id, new LifecycleNaturalTickObserver(world, tile)); } @@ -466,9 +466,9 @@ private void finishRoundTrip(RoundTripRequest request) { System.out.println("ADVLIFECYCLE COMPLETE " + request.operation + " " + request.label); } - private void roundTripTick(TickEvent.WorldTickEvent event) { + private void roundTripTick(TickEvent.LevelTickEvent event) { RoundTripRequest request = roundTrip; - if (request == null || event.world != request.world) return; + if (request == null || event.level != request.world) return; boolean finish = false; try { if (event.phase == TickEvent.Phase.START && !request.window.started) { @@ -499,14 +499,14 @@ private Set chunks() { } private void unload(ChunkEvent.Unload event) { - if (event.getWorld() instanceof ServerLevel) { + if (event.getLevel() instanceof ServerLevel) { increment(unloads, event.getChunk().getPos().toLong()); recordEvent(event, "unload"); } } private void load(ChunkEvent.Load event) { - if (event.getWorld() instanceof ServerLevel) { + if (event.getLevel() instanceof ServerLevel) { increment(loads, event.getChunk().getPos().toLong()); recordEvent(event, "load"); } @@ -517,7 +517,7 @@ private void recordEvent(ChunkEvent event, String operation) { Map record = new LinkedHashMap<>(); record.put("operation", operation); record.put("pos", Arrays.asList(event.getChunk().getPos().x, event.getChunk().getPos().z)); - record.put("worldTime", ((ServerLevel) event.getWorld()).getGameTime()); + record.put("worldTime", ((ServerLevel) event.getLevel()).getGameTime()); if (tracingLoads && "load".equals(operation)) { List stack = new ArrayList<>(); for (StackTraceElement frame : Thread.currentThread().getStackTrace()) { @@ -541,7 +541,7 @@ private Map witness(ServerLevel world, int[] coordinates) { LevelChunk chunk = world.getChunkSource().getChunkNow(pos.getX() >> 4, pos.getZ() >> 4); result.put("loaded", chunk != null); if (chunk == null) return result; - result.put("block", chunk.getBlockState(pos).getBlock().getRegistryName().toString()); + result.put("block", net.minecraftforge.registries.ForgeRegistries.BLOCKS.getKey(chunk.getBlockState(pos).getBlock()).toString()); result.put("state", chunk.getBlockState(pos).toString()); BlockEntity tile = chunk.getBlockEntity(pos, LevelChunk.EntityCreationType.CHECK); if (tile != null) { @@ -557,10 +557,10 @@ private Map witness(ServerLevel world, int[] coordinates) { return result; } - private void tick(TickEvent.WorldTickEvent event) { + private void tick(TickEvent.LevelTickEvent event) { roundTripTick(event); - if (!watching || !(event.world instanceof ServerLevel)) return; - ServerLevel world = (ServerLevel) event.world; + if (!watching || !(event.level instanceof ServerLevel)) return; + ServerLevel world = (ServerLevel) event.level; for (Fixture fixture : fixtures.values()) { if (fixture.from == null || completed.contains(fixture.id)) continue; int step = routeSteps.getOrDefault(fixture.id, 0); diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/MachineContext.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/MachineContext.java index 42bf1547..7a018f61 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/MachineContext.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/MachineContext.java @@ -231,7 +231,7 @@ JsonArray snapshot() { JsonObject row = new JsonObject(); row.addProperty("x", pos.getX()); row.addProperty("y", pos.getY()); row.addProperty("z", pos.getZ()); BlockState state = world.getBlockState(pos); - row.addProperty("block", state.getBlock().getRegistryName().toString()); + row.addProperty("block", net.minecraftforge.registries.ForgeRegistries.BLOCKS.getKey(state.getBlock()).toString()); row.addProperty("state", state.toString()); BlockEntity tile = world.getBlockEntity(pos); if (tile != null) row.addProperty("nbt", tile.saveWithFullMetadata().toString()); diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/MachineOperationalProbe.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/MachineOperationalProbe.java index 6e0490f2..fcd2bf91 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/MachineOperationalProbe.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/MachineOperationalProbe.java @@ -56,18 +56,18 @@ static void register() { MinecraftForge.EVENT_BUS.addListener(probe::entityJoining); } - private void chunkLoaded(net.minecraftforge.event.world.ChunkEvent.Load event) { - if (paused && event.getWorld() instanceof ServerLevel) { - ServerLevel loaded = (ServerLevel) event.getWorld(); + private void chunkLoaded(net.minecraftforge.event.level.ChunkEvent.Load event) { + if (paused && event.getLevel() instanceof ServerLevel) { + ServerLevel loaded = (ServerLevel) event.getLevel(); if (loaded.dimension() == net.minecraft.world.level.Level.OVERWORLD) { held.addAll(NativeBlockEntityTicks.drain(loaded)); } } } - private void entityJoining(net.minecraftforge.event.entity.EntityJoinWorldEvent event) { - if (paused && event.getWorld() instanceof ServerLevel - && event.getWorld().dimension() == net.minecraft.world.level.Level.OVERWORLD + private void entityJoining(net.minecraftforge.event.entity.EntityJoinLevelEvent event) { + if (paused && event.getLevel() instanceof ServerLevel + && event.getLevel().dimension() == net.minecraft.world.level.Level.OVERWORLD && !(event.getEntity() instanceof net.minecraft.world.entity.player.Player)) { heldEntities.hold(event.getEntity()); } @@ -154,10 +154,10 @@ private void setup(Run run) { } catch (Throwable failure) { fail(run, "setup", failure); } } - private void tick(TickEvent.WorldTickEvent event) { - if (event.world.isClientSide || !(event.world instanceof ServerLevel) - || event.world.dimension() != net.minecraft.world.level.Level.OVERWORLD) return; - ServerLevel current = (ServerLevel) event.world; + private void tick(TickEvent.LevelTickEvent event) { + if (event.level.isClientSide || !(event.level instanceof ServerLevel) + || event.level.dimension() != net.minecraft.world.level.Level.OVERWORLD) return; + ServerLevel current = (ServerLevel) event.level; if (event.phase == TickEvent.Phase.START && paused) { held.addAll(NativeBlockEntityTicks.drain(current)); for (Entity entity : current.getAllEntities()) if (!(entity instanceof net.minecraft.world.entity.player.Player)) heldEntities.hold(entity); @@ -278,7 +278,7 @@ private void inspectNext() { if (pos.getX() < 50000 || pos.getX() > 100000 || pos.getY() < 79 || pos.getY() > 111 || pos.getZ() < 50000 || pos.getZ() > 50031) throw new IllegalArgumentException("Saved position escaped proving ground"); actual.addProperty("x", pos.getX()); actual.addProperty("y", pos.getY()); actual.addProperty("z", pos.getZ()); - actual.addProperty("block", world.getBlockState(pos).getBlock().getRegistryName().toString()); + actual.addProperty("block", net.minecraftforge.registries.ForgeRegistries.BLOCKS.getKey(world.getBlockState(pos).getBlock()).toString()); actual.addProperty("state", world.getBlockState(pos).toString()); BlockEntity tile = world.getBlockEntity(pos); if (tile != null) actual.addProperty("nbt", tile.saveWithFullMetadata().toString()); diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/MigrationProbe.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/MigrationProbe.java index 159316cc..bf38d684 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/MigrationProbe.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/MigrationProbe.java @@ -20,7 +20,7 @@ import net.minecraft.world.level.Level; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.TickEvent; -import net.minecraftforge.event.world.WorldEvent; +import net.minecraftforge.event.level.LevelEvent; import net.minecraftforge.event.RegisterCommandsEvent; /** Read-only state witnesses in explicitly marked disposable migration worlds. */ @@ -37,14 +37,14 @@ static void register() { MinecraftForge.EVENT_BUS.addListener(probe::starting); } - private void load(WorldEvent.Load event) { - if (event.getWorld() instanceof Level) freeze((Level) event.getWorld()); + private void load(LevelEvent.Load event) { + if (event.getLevel() instanceof Level) freeze((Level) event.getLevel()); } - private void tick(TickEvent.WorldTickEvent event) { - if (event.phase == TickEvent.Phase.START) freeze(event.world); + private void tick(TickEvent.LevelTickEvent event) { + if (event.phase == TickEvent.Phase.START) freeze(event.level); } - private void chunkLoaded(net.minecraftforge.event.world.ChunkEvent.Load event) { - if (event.getWorld() instanceof Level) freeze((Level) event.getWorld()); + private void chunkLoaded(net.minecraftforge.event.level.ChunkEvent.Load event) { + if (event.getLevel() instanceof Level) freeze((Level) event.getLevel()); } private void freeze(Level world) { if (!world.isClientSide && !new File("migration-operate.allow").isFile()) NativeBlockEntityTicks.drain(world); @@ -75,7 +75,7 @@ private void snapshot(Level world, String label) throws Exception { JsonObject entry = JSON.fromJson(JSON.toJson(element), JsonObject.class); BlockPos pos = new BlockPos(entry.get("x").getAsInt(), entry.get("y").getAsInt(), entry.get("z").getAsInt()); BlockState state = world.getBlockState(pos); - entry.addProperty("actualName", state.getBlock().getRegistryName().toString()); + entry.addProperty("actualName", net.minecraftforge.registries.ForgeRegistries.BLOCKS.getKey(state.getBlock()).toString()); JsonObject properties = new JsonObject(); for (Property property : state.getProperties()) { properties.addProperty(property.getName(), state.getValue(property).toString().toLowerCase(Locale.ROOT)); diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/Native117Readiness.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/Native117Readiness.java index 869f0378..189115f7 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/Native117Readiness.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/Native117Readiness.java @@ -69,8 +69,8 @@ private void request(ServerLevel level, Bounds bounds, String requestedLabel) { System.out.println("ADVREADINESS QUEUED " + label); } - private void tick(TickEvent.WorldTickEvent event) { - if (observation == null || event.phase != TickEvent.Phase.END || event.world != world) return; + private void tick(TickEvent.LevelTickEvent event) { + if (observation == null || event.phase != TickEvent.Phase.END || event.level != world) return; List states = new ArrayList<>(); for (int x = observation.bounds.minX; x <= observation.bounds.maxX; x++) { for (int z = observation.bounds.minZ; z <= observation.bounds.maxZ; z++) { @@ -82,7 +82,7 @@ private void tick(TickEvent.WorldTickEvent event) { if (!observation.observe(world.getGameTime(), System.nanoTime(), states)) return; JsonObject report = observation.report(); report.addProperty("label", label); - report.addProperty("minecraft", "1.18.2"); + report.addProperty("minecraft", "1.19.4"); report.addProperty("dimension", world.dimension().location().toString()); try { Files.createDirectories(output.toPath().getParent()); diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/Native117StoppedEntitiesSnapshot.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/Native117StoppedEntitiesSnapshot.java index 28f07ede..0fdb3e02 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/Native117StoppedEntitiesSnapshot.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/Native117StoppedEntitiesSnapshot.java @@ -60,7 +60,7 @@ private static JsonObject header(MinecraftServer server, String boot, String pha JsonObject result = new JsonObject(); result.addProperty("format", 1); result.addProperty("minecraftVersion", SharedConstants.getCurrentVersion().getName()); - result.addProperty("dataVersion", SharedConstants.getCurrentVersion().getWorldVersion()); + result.addProperty("dataVersion", SharedConstants.getCurrentVersion().getDataVersion().getVersion()); result.addProperty("worldName", server.getWorldData().getLevelName()); result.addProperty("worldDirectory", server.getWorldPath(LevelResource.ROOT).toAbsolutePath().normalize().toString()); result.addProperty("bootId", boot); diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/Native117StoppedEntitiesSnapshotV2.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/Native117StoppedEntitiesSnapshotV2.java index 5daa55fc..67fc0a7c 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/Native117StoppedEntitiesSnapshotV2.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/Native117StoppedEntitiesSnapshotV2.java @@ -64,7 +64,7 @@ private static JsonObject header(MinecraftServer server, String boot, String pha JsonObject result = new JsonObject(); result.addProperty("format", 2); result.addProperty("minecraftVersion", SharedConstants.getCurrentVersion().getName()); - result.addProperty("dataVersion", SharedConstants.getCurrentVersion().getWorldVersion()); + result.addProperty("dataVersion", SharedConstants.getCurrentVersion().getDataVersion().getVersion()); result.addProperty("worldName", server.getWorldData().getLevelName()); result.addProperty("worldDirectory", server.getWorldPath(LevelResource.ROOT).toAbsolutePath().normalize().toString()); result.addProperty("bootId", boot); diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/Native117WorldgenAccess.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/Native117WorldgenAccess.java index aadc43a6..e0cb6721 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/Native117WorldgenAccess.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/Native117WorldgenAccess.java @@ -58,16 +58,16 @@ final class Native117WorldgenAccess implements WorldgenSamplingJob.WorldAccess { @Override public int minBuildHeight() { return world.getMinBuildHeight(); } @Override public boolean matchesBiome(String expected, int x, int y, int z) { - return WorksController.matchesExpectedBiome(world, expected, world.getBiome(cursor.set(x, y, z)).value()); + return WorksController.matchesExpectedBiome(expected, world.getBiome(cursor.set(x, y, z))); } @Override public CompletableFuture requestChunk(int x, int z, boolean counting) { return chunks.request(x, z).thenApply(chunk -> new WorldgenSamplingJob.SampleChunk() { - private Biome center; + private net.minecraft.core.Holder center; @Override public boolean centerMatches(String expected) { - if (center == null) center = world.getBiome(new BlockPos((x << 4) + 8, world.getSeaLevel(), (z << 4) + 8)).value(); - return WorksController.matchesExpectedBiome(world, expected, center); + if (center == null) center = world.getBiome(new BlockPos((x << 4) + 8, world.getSeaLevel(), (z << 4) + 8)); + return WorksController.matchesExpectedBiome(expected, center); } @Override public String blockName(int blockX, int y, int blockZ) { diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/Native118TestFlat.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/Native118TestFlat.java deleted file mode 100644 index 0eed8e6f..00000000 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/Native118TestFlat.java +++ /dev/null @@ -1,40 +0,0 @@ -package zone.moddev.mc.advantageworks; - -import java.util.List; -import java.util.Optional; -import net.minecraft.core.HolderSet; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.levelgen.FlatLevelSource; -import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; -import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; -import net.minecraft.world.level.levelgen.structure.StructureSet; -import net.minecraftforge.common.world.ForgeWorldPreset; -import net.minecraftforge.event.RegistryEvent; - -final class Native118TestFlat { - static final String ID = "advantageworkstest:test_flat"; - - private Native118TestFlat() {} - - static ForgeWorldPreset createType() { - return new ForgeWorldPreset((registries, seed, settings) -> { - // Forge 40 passes the level-type identifier rather than generator JSON. - if (!settings.isEmpty() && !settings.equals(ID)) { - throw new IllegalArgumentException("Unexpected settings for the test flat preset"); - } - Optional> structures = Optional.of(HolderSet.direct(List.of())); - var flat = new FlatLevelGeneratorSettings(structures, registries.registryOrThrow(Registry.BIOME_REGISTRY)); - flat.getLayersInfo().add(new FlatLayerInfo(1, Blocks.BEDROCK)); - flat.getLayersInfo().add(new FlatLayerInfo(2, Blocks.DIRT)); - flat.getLayersInfo().add(new FlatLayerInfo(1, Blocks.GRASS_BLOCK)); - flat.updateLayers(); - return new FlatLevelSource(registries.registryOrThrow(Registry.STRUCTURE_SET_REGISTRY), flat); - }).setRegistryName(new ResourceLocation(ID)); - } - - static void register(RegistryEvent.Register event) { - event.getRegistry().register(createType()); - } -} diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/NativeHarvestProbe.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/NativeHarvestProbe.java index 1cffdbf3..e627ad03 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/NativeHarvestProbe.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/NativeHarvestProbe.java @@ -136,7 +136,7 @@ private static void witness(ServerLevel world, ServerPlayer player, JsonArray ro JsonObject actual = new JsonObject(); actual.addProperty("entityId", entity.getId()); actual.addProperty("uuid", entity.getUUID().toString()); - actual.addProperty("item", entity.getItem().getItem().getRegistryName().toString()); + actual.addProperty("item", net.minecraftforge.registries.ForgeRegistries.ITEMS.getKey(entity.getItem().getItem()).toString()); actual.addProperty("count", entity.getItem().getCount()); actual.addProperty("removed", entity.isRemoved()); actualDrops.add(actual); @@ -145,7 +145,7 @@ private static void witness(ServerLevel world, ServerPlayer player, JsonArray ro int count = 0; for (ItemEntity entity : drops) { ItemStack stack = entity.getItem(); - require(stack.getItem().getRegistryName().toString().equals(dropId), "Wrong actual drop: " + stack); + require(net.minecraftforge.registries.ForgeRegistries.ITEMS.getKey(stack.getItem()).toString().equals(dropId), "Wrong actual drop: " + stack); count += stack.getCount(); } require(count >= minimum && count <= maximum, "Actual drop quantity changed: " + count); @@ -218,7 +218,7 @@ private static void run(ServerLevel world, String label) throws Exception { harvestDrops(world, new AABB(POS).inflate(2)).forEach(ItemEntity::discard); JsonObject report = new JsonObject(); report.addProperty("label", label); - report.addProperty("minecraft", "1.18.2"); + report.addProperty("minecraft", "1.19.4"); report.addProperty("worldTime", world.getGameTime()); report.addProperty("fixtureIsolation", "native117-live-drops-v1"); report.addProperty("status", java.util.stream.StreamSupport.stream(rows.spliterator(), false) diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/NativeInteropProbe.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/NativeInteropProbe.java index ff9e942c..c4069727 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/NativeInteropProbe.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/NativeInteropProbe.java @@ -25,13 +25,10 @@ import net.minecraft.server.level.ServerLevel; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.util.LazyOptional; -import net.minecraftforge.energy.CapabilityEnergy; import net.minecraftforge.energy.IEnergyStorage; import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.event.RegisterCommandsEvent; -import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.registries.ForgeRegistries; import zone.moddev.mc.basepower.machines.conveyors.BlockConveyor; @@ -93,7 +90,7 @@ private static void place(ServerLevel world, BlockPos pos, BlockState state) { } private static IFluidHandler fluid(BlockEntity tile) { - return tile.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, Direction.UP) + return tile.getCapability(net.minecraftforge.common.capabilities.ForgeCapabilities.FLUID_HANDLER, Direction.UP) .orElseThrow(() -> new AssertionError("Missing native fluid capability")); } @@ -107,7 +104,7 @@ private static void energy(ServerLevel world, JsonArray checks) { place(world, pos, block("basepower:" + id).defaultBlockState()); TileEntityRFConverter tile = (TileEntityRFConverter) world.getBlockEntity(pos); check(checks, id + "/capability", () -> { - IEnergyStorage storage = tile.getCapability(CapabilityEnergy.ENERGY, Direction.NORTH) + IEnergyStorage storage = tile.getCapability(net.minecraftforge.common.capabilities.ForgeCapabilities.ENERGY, Direction.NORTH) .orElseThrow(() -> new AssertionError("Missing Forge Energy capability")); require(storage == tile, "Capability does not expose the live converter"); require(storage.receiveEnergy(-1, false) == 0 && storage.extractEnergy(-1, false) == 0, @@ -145,12 +142,12 @@ private static void energy(ServerLevel world, JsonArray checks) { "Reverse conversion lost energy: before=" + before + ", after=" + after); }); check(checks, id + "/invalidation", () -> { - LazyOptional old = tile.getCapability(CapabilityEnergy.ENERGY, Direction.NORTH); + LazyOptional old = tile.getCapability(net.minecraftforge.common.capabilities.ForgeCapabilities.ENERGY, Direction.NORTH); require(old.isPresent(), "Missing original capability"); world.setBlock(pos, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState(), 3); require(!old.isPresent(), "Removed converter still exposes its stale capability"); world.setBlock(pos, block("basepower:" + id).defaultBlockState(), 3); - LazyOptional replacement = world.getBlockEntity(pos).getCapability(CapabilityEnergy.ENERGY, Direction.NORTH); + LazyOptional replacement = world.getBlockEntity(pos).getCapability(net.minecraftforge.common.capabilities.ForgeCapabilities.ENERGY, Direction.NORTH); require(replacement.isPresent() && replacement != old, "Replacement did not expose a fresh capability"); }); } @@ -176,7 +173,7 @@ private static void tank(ServerLevel world, JsonArray checks) { && tank.getFluidInTank(0).getAmount() == 500, "Drain lost fluid or identity"); }); check(checks, "portable-tank/native-break-drop-and-replace", () -> { - LazyOptional old = tile.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, Direction.UP); + LazyOptional old = tile.getCapability(net.minecraftforge.common.capabilities.ForgeCapabilities.FLUID_HANDLER, Direction.UP); require(world.destroyBlock(TANK, true), "Native block destruction failed"); require(!old.isPresent(), "Removed tank still exposes a stale capability"); List drops = world.getEntitiesOfClass(ItemEntity.class, new AABB(TANK).inflate(2)); @@ -229,10 +226,10 @@ private static final class CallbackTank extends StorageTankTileEntity { } private static CallbackTank callbackTank() { - require(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY != null - && CapabilityItemHandler.ITEM_HANDLER_CAPABILITY != null, + require(net.minecraftforge.common.capabilities.ForgeCapabilities.FLUID_HANDLER != null + && net.minecraftforge.common.capabilities.ForgeCapabilities.ITEM_HANDLER != null, "Forge did not inject native fluid and item capability tokens"); - require((Object) CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY != CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, + require((Object) net.minecraftforge.common.capabilities.ForgeCapabilities.FLUID_HANDLER != net.minecraftforge.common.capabilities.ForgeCapabilities.ITEM_HANDLER, "Native fluid and item capability tokens alias each other"); CallbackTank tile = new CallbackTank(); require(tile.getLevel() == null && !tile.isRemoved(), "Callback fixture is not a fresh standalone tank"); @@ -246,12 +243,12 @@ private static CallbackTank callbackTank() { private static void requireUnavailable(CallbackTank tile) { for (Direction face : Direction.values()) { - require(!tile.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, face).isPresent() - && !tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, face).isPresent(), + require(!tile.getCapability(net.minecraftforge.common.capabilities.ForgeCapabilities.FLUID_HANDLER, face).isPresent() + && !tile.getCapability(net.minecraftforge.common.capabilities.ForgeCapabilities.ITEM_HANDLER, face).isPresent(), "Unloaded callback tank recreated a sided capability: " + face); } - require(!tile.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, null).isPresent() - && !tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null).isPresent(), + require(!tile.getCapability(net.minecraftforge.common.capabilities.ForgeCapabilities.FLUID_HANDLER, null).isPresent() + && !tile.getCapability(net.minecraftforge.common.capabilities.ForgeCapabilities.ITEM_HANDLER, null).isPresent(), "Unloaded callback tank recreated an unsided capability"); } @@ -259,8 +256,8 @@ private static void capabilityCallbacks(JsonArray checks) { // These unplaced tanks witness native callbacks, not physical chunk unloading. check(checks, "capability/native-chunk-unloaded-callback", () -> { CallbackTank tile = callbackTank(); - LazyOptional oldFluid = tile.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, Direction.UP); - LazyOptional oldItems = tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, Direction.DOWN); + LazyOptional oldFluid = tile.getCapability(net.minecraftforge.common.capabilities.ForgeCapabilities.FLUID_HANDLER, Direction.UP); + LazyOptional oldItems = tile.getCapability(net.minecraftforge.common.capabilities.ForgeCapabilities.ITEM_HANDLER, Direction.DOWN); require(oldFluid.isPresent() && oldItems.isPresent(), "Standalone tank lacks native capability wrappers"); CompoundTag contents = tile.getTank().writeToNBT(new CompoundTag()); tile.onChunkUnloaded(); @@ -273,8 +270,8 @@ private static void capabilityCallbacks(JsonArray checks) { check(checks, "capability/native-clear-removed-and-revive", () -> { for (boolean clearRemoved : new boolean[] {true, false}) { CallbackTank tile = callbackTank(); - LazyOptional oldFluid = tile.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, Direction.UP); - LazyOptional oldItems = tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, Direction.DOWN); + LazyOptional oldFluid = tile.getCapability(net.minecraftforge.common.capabilities.ForgeCapabilities.FLUID_HANDLER, Direction.UP); + LazyOptional oldItems = tile.getCapability(net.minecraftforge.common.capabilities.ForgeCapabilities.ITEM_HANDLER, Direction.DOWN); IFluidHandler oldFluidWrapper = oldFluid.orElseThrow(() -> new AssertionError("Missing original fluid wrapper")); IItemHandler oldItemWrapper = oldItems.orElseThrow(() -> new AssertionError("Missing original item wrapper")); FluidStack before = oldFluidWrapper.getFluidInTank(0); @@ -289,8 +286,8 @@ private static void capabilityCallbacks(JsonArray checks) { requireUnavailable(tile); if (clearRemoved) tile.clearRemoved(); else tile.reviveNativeCapabilities(); - LazyOptional currentFluid = tile.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, Direction.UP); - LazyOptional currentItems = tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, Direction.DOWN); + LazyOptional currentFluid = tile.getCapability(net.minecraftforge.common.capabilities.ForgeCapabilities.FLUID_HANDLER, Direction.UP); + LazyOptional currentItems = tile.getCapability(net.minecraftforge.common.capabilities.ForgeCapabilities.ITEM_HANDLER, Direction.DOWN); IFluidHandler currentFluidWrapper = currentFluid.orElseThrow(() -> new AssertionError("Revival did not restore fluid capability")); IItemHandler currentItemWrapper = currentItems.orElseThrow(() -> new AssertionError("Revival did not restore item capability")); require(!tile.isRemoved() && currentFluid != oldFluid && currentItems != oldItems diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/ProcessProgression.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/ProcessProgression.java index 05f2af2f..5592c224 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/ProcessProgression.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/ProcessProgression.java @@ -129,7 +129,7 @@ private static JsonObject hammerRecipeSelection(ServerLevel world, ItemStack ham Class hammerClass = hammer.getItem().getClass(); if (!hammerClass.getName().equals("zone.moddev.mc.basemetals.content.CrackhammerItem")) throw new IllegalStateException("Unexpected hammer class " + hammerClass.getName()); - RecipeType type = net.minecraft.core.Registry.RECIPE_TYPE + RecipeType type = net.minecraft.core.registries.BuiltInRegistries.RECIPE_TYPE .getOptional(new ResourceLocation("basemetals", "crushing")).orElseThrow(); Recipe selected = world.getRecipeManager().getRecipeFor( (RecipeType>) type, new SimpleContainer(input.copy()), world).orElse(null); @@ -138,7 +138,8 @@ private static JsonObject hammerRecipeSelection(ServerLevel world, ItemStack ham if (selected != null) { selection.addProperty("id", selected.getId().toString()); selection.addProperty("class", selected.getClass().getName()); - selection.addProperty("resultNbt", selected.getResultItem().save(new net.minecraft.nbt.CompoundTag()).toString()); + selection.addProperty("resultNbt", selected.getResultItem(world.registryAccess()) + .save(new net.minecraft.nbt.CompoundTag()).toString()); } } catch (RuntimeException | LinkageError error) { selection.addProperty("status", "OBSERVER_ERROR"); diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/ProgressionProbe.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/ProgressionProbe.java index 475704b6..a2cbc194 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/ProgressionProbe.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/ProgressionProbe.java @@ -130,7 +130,7 @@ private void audit(MinecraftServer server, ServerLevel world, String label) thro changed = false; for (Recipe recipe : recipes) { if (executed.contains(recipe.getId().toString()) || recipe.getIngredients().isEmpty()) continue; - String type = net.minecraft.core.Registry.RECIPE_TYPE.getKey(recipe.getType()).toString(); + String type = net.minecraft.core.registries.BuiltInRegistries.RECIPE_TYPE.getKey(recipe.getType()).toString(); boolean crafting = recipe.getType() == RecipeType.CRAFTING; boolean smelting = recipe.getType() == RecipeType.SMELTING; boolean crushing = type.equals("basemetals:crushing"); @@ -154,6 +154,9 @@ private void audit(MinecraftServer server, ServerLevel world, String label) thro if (crafting) { AbstractContainerMenu container = new AbstractContainerMenu(null, 0) { @Override public boolean stillValid(net.minecraft.world.entity.player.Player p) { return true; } + @Override public ItemStack quickMoveStack(net.minecraft.world.entity.player.Player p, int slot) { + return ItemStack.EMPTY; + } }; for (int width = 1; width <= 3 && result.isEmpty(); width++) { if (ingredients.size() > width * 3) continue; @@ -188,8 +191,8 @@ private void audit(MinecraftServer server, ServerLevel world, String label) thro JsonObject exclusions = fixture.getAsJsonObject("exclusions"); int blocked = 0; for (Item item : ForgeRegistries.ITEMS.getValues()) { - if (!owned(item.getRegistryName())) continue; - String id = item.getRegistryName().toString(); + if (!owned(net.minecraftforge.registries.ForgeRegistries.ITEMS.getKey(item))) continue; + String id = net.minecraftforge.registries.ForgeRegistries.ITEMS.getKey(item).toString(); JsonObject row = new JsonObject(); row.addProperty("id", id); if (exclusions.has(id)) { @@ -228,8 +231,8 @@ private void audit(MinecraftServer server, ServerLevel world, String label) thro JsonObject tags = new JsonObject(); for (String name : new String[] {"wires", "dusts/carbon", "rubber", "plastic", "circuit_boards", "solder"}) { List members = new ArrayList<>(); - for (var item : net.minecraft.core.Registry.ITEM.getTagOrEmpty(ItemTags.create(new ResourceLocation("forge", name)))) - members.add(item.value().getRegistryName().toString()); + for (var item : net.minecraft.core.registries.BuiltInRegistries.ITEM.getTagOrEmpty(ItemTags.create(new ResourceLocation("forge", name)))) + members.add(net.minecraftforge.registries.ForgeRegistries.ITEMS.getKey(item.value()).toString()); Collections.sort(members); tags.add(name, JSON.toJsonTree(members)); } @@ -262,7 +265,7 @@ static ItemStack stack(String id) { } private static LootContext chestContext(ServerLevel world, int seed) { - return new LootContext.Builder(world).withRandom(new Random(seed)) + return new LootContext.Builder(world).withRandom(net.minecraft.util.RandomSource.create(seed)) .withParameter(net.minecraft.world.level.storage.loot.parameters.LootContextParams.ORIGIN, new net.minecraft.world.phys.Vec3(0, 80, 0)) .create(net.minecraft.world.level.storage.loot.parameters.LootContextParamSets.CHEST); } @@ -270,14 +273,14 @@ private static LootContext chestContext(ServerLevel world, int seed) { @SuppressWarnings("unchecked") private static ItemStack matchedResult(Recipe recipe, net.minecraft.world.Container inventory, ServerLevel world) { Recipe typed = (Recipe) recipe; - return typed.matches(inventory, world) ? typed.assemble(inventory) : ItemStack.EMPTY; + return typed.matches(inventory, world) ? typed.assemble(inventory, world.registryAccess()) : ItemStack.EMPTY; } - private static boolean advantage(ItemStack stack) { return !stack.isEmpty() && owned(stack.getItem().getRegistryName()); } + private static boolean advantage(ItemStack stack) { return !stack.isEmpty() && owned(net.minecraftforge.registries.ForgeRegistries.ITEMS.getKey(stack.getItem())); } private static boolean owned(ResourceLocation id) { return id != null && (id.getNamespace().equals("basepower") || id.getNamespace().equals("basesteam") || id.getNamespace().equals("baseelectric")); } - static String id(ItemStack stack) { return stack.getItem().getRegistryName().toString(); } + static String id(ItemStack stack) { return net.minecraftforge.registries.ForgeRegistries.ITEMS.getKey(stack.getItem()).toString(); } static void require(boolean value, String reason) { if (!value) throw new IllegalStateException(reason); } } diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/RecipeBookProbe.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/RecipeBookProbe.java index 39dfb81e..cf822174 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/RecipeBookProbe.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/RecipeBookProbe.java @@ -28,14 +28,14 @@ static void verify(MinecraftServer server, ServerLevel world, String label, Json String namespace = recipe.getId().getNamespace(); if (!(namespace.equals("basepower") || namespace.equals("basesteam") || namespace.equals("baseelectric"))) continue; if (recipe.getType() != RecipeType.CRAFTING && recipe.getType() != RecipeType.SMELTING) continue; - String output = id(recipe.getResultItem()); + String output = id(recipe.getResultItem(world.registryAccess())); require(output.equals(recipe.getGroup()), "Mixed-output recipe group: " + recipe.getId() + " -> " + recipe.getGroup()); JsonObject entry = new JsonObject(); entry.addProperty("id", recipe.getId().toString()); entry.addProperty("group", recipe.getGroup()); - entry.addProperty("type", net.minecraft.core.Registry.RECIPE_TYPE.getKey(recipe.getType()).toString()); + entry.addProperty("type", net.minecraft.core.registries.BuiltInRegistries.RECIPE_TYPE.getKey(recipe.getType()).toString()); entry.addProperty("output", output); - entry.addProperty("count", recipe.getResultItem().getCount()); + entry.addProperty("count", recipe.getResultItem(world.registryAccess()).getCount()); entries.add(entry); recipes.add(recipe); } @@ -59,7 +59,7 @@ static void verify(MinecraftServer server, ServerLevel world, String label, Json require(starch.matches(grid, world), "Potato recipe rejected slot/stack " + slot + "/" + count); Recipe selected = world.getRecipeManager().getRecipeFor(RecipeType.CRAFTING, grid, world).orElse(null); require(selected != null && selected.getId().equals(starch.getId()), "Conflicting native potato crafting recipe"); - ItemStack result = starch.assemble(grid); + ItemStack result = starch.assemble(grid, world.registryAccess()); require(id(result).equals("basepower:starch") && result.getCount() == 1, "Starch output changed"); require(grid.getItem(slot).getCount() == count, "Result preview mutated input"); ItemStack consumed = grid.removeItem(slot, 1); @@ -87,6 +87,9 @@ static void verify(MinecraftServer server, ServerLevel world, String label, Json private static CraftingContainer grid(int width) { return new CraftingContainer(new AbstractContainerMenu(null, 0) { @Override public boolean stillValid(net.minecraft.world.entity.player.Player player) { return true; } + @Override public ItemStack quickMoveStack(net.minecraft.world.entity.player.Player player, int slot) { + return ItemStack.EMPTY; + } }, width, width); } } diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/RecipeUnlockProbe.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/RecipeUnlockProbe.java index fb95eb9e..76481d73 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/RecipeUnlockProbe.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/RecipeUnlockProbe.java @@ -132,7 +132,7 @@ private void run(MinecraftServer server, ServerLevel world, String operation, St if (choices.isEmpty()) { emptyTags++; continue; } for (Item candidate : choices) { player.getInventory().setItem(0, new ItemStack(candidate)); - require(matchesInventory(condition, player.getInventory()), "Tag member rejected: " + id + " / " + candidate.getRegistryName()); + require(matchesInventory(condition, player.getInventory()), "Tag member rejected: " + id + " / " + net.minecraftforge.registries.ForgeRegistries.ITEMS.getKey(candidate)); members++; } reset(advancement, recipe); @@ -176,7 +176,7 @@ private void run(MinecraftServer server, ServerLevel world, String operation, St if (!choices.isEmpty()) { Item candidate = choices.get(0); discover(candidate); - additions.add(candidate.getRegistryName().toString()); + additions.add(net.minecraftforge.registries.ForgeRegistries.ITEMS.getKey(candidate).toString()); break; } } @@ -258,9 +258,9 @@ private static boolean matchesInventory(InventoryChangeTrigger.TriggerInstance c private static List choices(JsonObject predicate) { if (predicate.has("item")) return Collections.singletonList(item(predicate.get("item").getAsString())); List items = new ArrayList<>(); - for (var holder : net.minecraft.core.Registry.ITEM.getTagOrEmpty( + for (var holder : net.minecraft.core.registries.BuiltInRegistries.ITEM.getTagOrEmpty( ItemTags.create(new ResourceLocation(predicate.get("tag").getAsString())))) items.add(holder.value()); - items.sort(Comparator.comparing(candidate -> candidate.getRegistryName().toString())); + items.sort(Comparator.comparing(candidate -> net.minecraftforge.registries.ForgeRegistries.ITEMS.getKey(candidate).toString())); return items; } diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/RedstoneReloadProbe.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/RedstoneReloadProbe.java index 3d8d7c2a..d77a698b 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/RedstoneReloadProbe.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/RedstoneReloadProbe.java @@ -112,9 +112,9 @@ private void commands(RegisterCommandsEvent event) { private boolean paused() { return phase != Phase.SEEDING && phase != Phase.OPERATING; } - private void tick(TickEvent.WorldTickEvent event) { - if (!(event.world instanceof ServerLevel) || event.world.dimension() != Level.OVERWORLD) return; - ServerLevel current = (ServerLevel) event.world; + private void tick(TickEvent.LevelTickEvent event) { + if (!(event.level instanceof ServerLevel) || event.level.dimension() != Level.OVERWORLD) return; + ServerLevel current = (ServerLevel) event.level; if (event.phase == TickEvent.Phase.START) { tickStart = current.getGameTime(); if (paused()) { @@ -218,7 +218,7 @@ private void construct(Witness witness) { force(witness); for (int dx = -1; dx <= 1; dx++) for (int dy = -1; dy <= 1; dy++) for (int dz = -1; dz <= 1; dz++) world.setBlock(witness.pos.offset(dx, dy, dz), Blocks.AIR.defaultBlockState(), 2); - Block block = Registry.BLOCK.get(new ResourceLocation("baseelectric:electric_drill")); + Block block = net.minecraft.core.registries.BuiltInRegistries.BLOCK.get(new ResourceLocation("baseelectric:electric_drill")); if (block == Blocks.AIR) throw new IllegalStateException("Electric drill registry absent"); world.setBlock(witness.pos, block.defaultBlockState().setValue(BlockStateProperties.FACING, Direction.NORTH), 2); for (int dx = -1; dx <= 1; dx++) for (int dy = -1; dy <= 1; dy++) @@ -280,11 +280,11 @@ private List> snapshots() { row.put("pos", coordinates(witness.pos)); row.put("held", witness.held); row.put("offset", witness.offset); - row.put("block", state.getBlock().getRegistryName().toString()); + row.put("block", net.minecraftforge.registries.ForgeRegistries.BLOCKS.getKey(state.getBlock()).toString()); row.put("state", state.toString()); row.put("active", active(state)); row.put("signal", world.hasNeighborSignal(witness.pos)); - row.put("holdBlock", world.getBlockState(witness.pos.above()).getBlock().getRegistryName().toString()); + row.put("holdBlock", net.minecraftforge.registries.ForgeRegistries.BLOCKS.getKey(world.getBlockState(witness.pos.above()).getBlock()).toString()); row.put("energy", ((PoweredEntity) tile).getEnergy(new ConduitType("electricity"))); row.put("progress", (int) nbt.getShort("progress")); row.put("nbt", nbt.toString()); diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/SteamOperationalActors.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/SteamOperationalActors.java index 02731d05..9fd7dd72 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/SteamOperationalActors.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/SteamOperationalActors.java @@ -78,7 +78,7 @@ private static void elevator(List cases) { BlockState state = c.world.getBlockState(c.at(8, y, 8)); c.record("shaft/" + y, state.toString()); if (movedUp && y <= 6) { - c.require("basesteam:platform".equals(String.valueOf(state.getBlock().getRegistryName())), + c.require("basesteam:platform".equals(String.valueOf(net.minecraftforge.registries.ForgeRegistries.BLOCKS.getKey(state.getBlock()))), "Raised shaft contains physical platform segment " + y); IntegerProperty height = (IntegerProperty) state.getBlock().getStateDefinition().getProperty("height"); c.require(height != null && state.getValue(height) == (y == 6 ? 4 : 0), diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/SteamOperationalCases.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/SteamOperationalCases.java index ff671622..2135796a 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/SteamOperationalCases.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/SteamOperationalCases.java @@ -8,7 +8,6 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.core.Direction; import net.minecraft.resources.ResourceLocation; -import net.minecraftforge.fluids.capability.CapabilityFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.registries.ForgeRegistries; @@ -64,7 +63,7 @@ private static void fill(MachineContext c, String id, int amount) { Fluid fluid = ForgeRegistries.FLUIDS.getValue(new ResourceLocation(id)); c.require(fluid != null, "Registered input fluid " + id); IFluidHandler handler = c.tile(X, Y, Z).getCapability( - CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, Direction.UP) + net.minecraftforge.common.capabilities.ForgeCapabilities.FLUID_HANDLER, Direction.UP) .orElseThrow(() -> new IllegalStateException("Missing Steam input fluid capability")); int accepted = handler.fill(new net.minecraftforge.fluids.FluidStack(fluid, amount), IFluidHandler.FluidAction.EXECUTE); @@ -91,7 +90,7 @@ private static void reserve(MachineContext c, String key, String fluid, int amou private static void slot(MachineContext c, int index, String item, int count) { ItemStack stack = c.inventory(X, Y, Z).getItem(index); - String actual = stack.isEmpty() ? "empty" : String.valueOf(stack.getItem().getRegistryName()); + String actual = stack.isEmpty() ? "empty" : String.valueOf(net.minecraftforge.registries.ForgeRegistries.ITEMS.getKey(stack.getItem())); c.record("slot/" + index, actual + ":" + stack.getCount()); c.require(count == 0 ? stack.isEmpty() : actual.equals(item) && stack.getCount() == count, @@ -294,7 +293,7 @@ private static void still(List cases) { add(cases, "still-invalid-fluid", "A real capability rejects water as still feedstock.", 120, c -> { machine(c, "steam_still"); supply(c, 500); IFluidHandler handler = c.tile(X, Y, Z).getCapability( - CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, Direction.UP) + net.minecraftforge.common.capabilities.ForgeCapabilities.FLUID_HANDLER, Direction.UP) .orElseThrow(() -> new IllegalStateException("Missing still fluid capability")); int accepted = handler.fill(new net.minecraftforge.fluids.FluidStack(net.minecraft.world.level.material.Fluids.WATER, 100), IFluidHandler.FluidAction.EXECUTE); @@ -323,7 +322,7 @@ private static int sources(MachineContext c) { } private static boolean blockIs(MachineContext c, int x, int y, int z, String id) { - String actual = String.valueOf(c.world.getBlockState(c.at(x, y, z)).getBlock().getRegistryName()); + String actual = String.valueOf(net.minecraftforge.registries.ForgeRegistries.BLOCKS.getKey(c.world.getBlockState(c.at(x, y, z)).getBlock())); c.record("block/" + x + "/" + y + "/" + z, actual); return actual.equals(id); } diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/WorksController.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/WorksController.java index d050d9fb..b21af06c 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/WorksController.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/WorksController.java @@ -29,13 +29,15 @@ import net.minecraft.world.phys.AABB; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Style; -import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ClickEvent; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.Level; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraftforge.common.BiomeDictionary; +import net.minecraft.core.Holder; +import net.minecraft.tags.BiomeTags; +import net.minecraftforge.common.Tags; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.TickEvent; import net.minecraftforge.fml.ModList; @@ -80,7 +82,7 @@ static WorksController getInstance() { synchronized void loadDefinition() { if (definition != null) return; - definition = WorksDefinitionLoader.loadNative118(); + definition = WorksDefinitionLoader.loadNative119(); for (WorksDefinition.Station station : definition.stations) { stations.put(station.id.toUpperCase(Locale.ROOT), station); statuses.put(station.id, "NOT_BUILT"); @@ -256,15 +258,13 @@ private synchronized void worldgenStopping(ServerStoppingEvent event) { worldgenJob.release(); } - static boolean matchesExpectedBiome(ServerLevel world, String expected, Biome biome) { - net.minecraft.resources.ResourceKey key = world.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY) - .getResourceKey(biome).orElseThrow(() -> new IllegalStateException("Unregistered native biome")); - if ("DESERT".equals(expected)) { - return BiomeDictionary.hasType(key, BiomeDictionary.Type.getType("SANDY")) - && !BiomeDictionary.hasType(key, BiomeDictionary.Type.getType("BEACH")) - && !BiomeDictionary.hasType(key, BiomeDictionary.Type.getType("MESA")); - } - return BiomeDictionary.hasType(key, BiomeDictionary.Type.getType(expected)); + static boolean matchesExpectedBiome(String expected, Holder biome) { + return switch (expected) { + case "DESERT" -> biome.is(Tags.Biomes.IS_SANDY) + && !biome.is(BiomeTags.IS_BEACH) && !biome.is(BiomeTags.IS_BADLANDS); + case "OCEAN" -> biome.is(BiomeTags.IS_OCEAN); + default -> throw new IllegalArgumentException("No biome test for " + expected); + }; } List stationIdsWithAll() { @@ -704,7 +704,7 @@ private void applyActions(ServerLevel world, WorksDefinition.Station station, world.addFreshEntity(entityItem); break; case "spawnEntity": - EntityType entityType = ForgeRegistries.ENTITIES.getValue(new ResourceLocation(action.entity)); + EntityType entityType = ForgeRegistries.ENTITY_TYPES.getValue(new ResourceLocation(action.entity)); Entity entity = entityType == null ? null : entityType.create(world); if (entity == null) throw new IllegalStateException("Unknown entity " + action.entity); entity.setPos(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5); @@ -823,10 +823,10 @@ private static void placeCommandSign(ServerLevel world, BlockPos pos, String lab BlockEntity tile = world.getBlockEntity(pos); if (!(tile instanceof SignBlockEntity)) return; SignBlockEntity sign = (SignBlockEntity) tile; - TextComponent clickable = new TextComponent(label); + net.minecraft.network.chat.MutableComponent clickable = Component.literal(label); clickable.setStyle(Style.EMPTY.withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, command))); sign.setMessage(0, clickable); - sign.setMessage(1, new TextComponent("right-click")); + sign.setMessage(1, Component.literal("right-click")); sign.setChanged(); } @@ -836,7 +836,7 @@ private static void placeSign(ServerLevel world, BlockPos pos, String... lines) if (!(tile instanceof SignBlockEntity)) return; SignBlockEntity sign = (SignBlockEntity) tile; for (int i = 0; i < 4; i++) { - sign.setMessage(i, new TextComponent(i < lines.length ? trimSign(lines[i]) : "")); + sign.setMessage(i, Component.literal(i < lines.length ? trimSign(lines[i]) : "")); } sign.setChanged(); } @@ -878,7 +878,7 @@ private static Item item(String name) { } private static CommandRuntimeException commandError(String message) { - return new CommandRuntimeException(new TextComponent(message)); + return new CommandRuntimeException(Component.literal(message)); } private static BlockState legacyState(String name, Block block, int metadata) { @@ -902,15 +902,15 @@ private static Fluid fluid(String name) { if (name == null || name.trim().isEmpty()) return null; ResourceLocation id = name.indexOf(':') >= 0 ? new ResourceLocation(name) : new ResourceLocation("basepower", name); - Fluid value = Registry.FLUID.getOptional(id).orElse(null); + Fluid value = net.minecraft.core.registries.BuiltInRegistries.FLUID.getOptional(id).orElse(null); if (value == null) { String migratedName = LegacyMigrationApi.fluidTarget(name); if (!name.equals(migratedName)) { - value = Registry.FLUID.getOptional(new ResourceLocation(migratedName)).orElse(null); + value = net.minecraft.core.registries.BuiltInRegistries.FLUID.getOptional(new ResourceLocation(migratedName)).orElse(null); } } if (value == null && name.indexOf(':') < 0) { - value = Registry.FLUID.getOptional(new ResourceLocation("minecraft", name)).orElse(null); + value = net.minecraft.core.registries.BuiltInRegistries.FLUID.getOptional(new ResourceLocation("minecraft", name)).orElse(null); } return value; } diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/WorksDefinitionLoader.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/WorksDefinitionLoader.java index 442246b6..891a7e1e 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/WorksDefinitionLoader.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/WorksDefinitionLoader.java @@ -38,8 +38,8 @@ static WorksDefinition loadNative117() { return load("/assets/advantageworkstest/works/works-1.17.1.json", "1.17.1"); } - static WorksDefinition loadNative118() { - return load("/assets/advantageworkstest/works/works-1.18.2.json", "1.18.2"); + static WorksDefinition loadNative119() { + return load("/assets/advantageworkstest/works/works-1.19.4.json", "1.19.4"); } static WorksDefinition load(String resource, String minecraft) { diff --git a/test-rig/src/main/java/zone/moddev/mc/advantageworks/WorksResultWriter.java b/test-rig/src/main/java/zone/moddev/mc/advantageworks/WorksResultWriter.java index 5e037290..eefe3101 100644 --- a/test-rig/src/main/java/zone/moddev/mc/advantageworks/WorksResultWriter.java +++ b/test-rig/src/main/java/zone/moddev/mc/advantageworks/WorksResultWriter.java @@ -31,7 +31,7 @@ File write(String stationId, String kind, Map payload) { Map document = new LinkedHashMap<>(); document.put("format", 1); document.put("recordedAtUtc", utcNow()); - document.put("minecraft", "1.18.2"); + document.put("minecraft", "1.19.4"); document.put("forge", net.minecraftforge.versions.forge.ForgeVersion.getVersion()); document.put("mods", loadedMods()); document.put("station", stationId); diff --git a/test-rig/src/main/resources/META-INF/mods.toml b/test-rig/src/main/resources/META-INF/mods.toml index dc07cc3d..8ceb14ec 100644 --- a/test-rig/src/main/resources/META-INF/mods.toml +++ b/test-rig/src/main/resources/META-INF/mods.toml @@ -1,5 +1,5 @@ modLoader="javafml" -loaderVersion="[40,)" +loaderVersion="[45,46)" license="LGPL-3.0-only" [[mods]] @@ -12,20 +12,20 @@ description='''Server-side commissioning harness for the Advantage family. Not f [[dependencies.advantageworkstest]] modId="basepower" mandatory=true -versionRange="[2.4.10.118021,)" +versionRange="[2.4.11.119041,3.0.0)" ordering="AFTER" side="SERVER" [[dependencies.advantageworkstest]] modId="forge" mandatory=true -versionRange="[40.3.0,41)" +versionRange="[45.4.0,46)" ordering="NONE" side="SERVER" [[dependencies.advantageworkstest]] modId="minecraft" mandatory=true -versionRange="[1.18.2]" +versionRange="[1.19.4]" ordering="NONE" side="SERVER" diff --git a/test-rig/src/main/resources/assets/advantageworkstest/works/flat-generator.json b/test-rig/src/main/resources/assets/advantageworkstest/works/flat-generator.json new file mode 100644 index 00000000..3eb0f3f4 --- /dev/null +++ b/test-rig/src/main/resources/assets/advantageworkstest/works/flat-generator.json @@ -0,0 +1,9 @@ +{ + "layers": [ + {"height": 1, "block": "minecraft:bedrock"}, + {"height": 2, "block": "minecraft:dirt"}, + {"height": 1, "block": "minecraft:grass_block"} + ], + "biome": "minecraft:plains", + "structure_overrides": [] +} diff --git a/test-rig/src/main/resources/assets/advantageworkstest/works/works-1.19.4.json b/test-rig/src/main/resources/assets/advantageworkstest/works/works-1.19.4.json new file mode 100644 index 00000000..c9c2099a --- /dev/null +++ b/test-rig/src/main/resources/assets/advantageworkstest/works/works-1.19.4.json @@ -0,0 +1,6 @@ +{ + "format": 1, + "minecraft": "1.19.4", + "title": "Advantage Works 1.19.4 Commissioning Ground", + "stationSource": "/assets/advantageworkstest/works/works-1.13.2.json" +} diff --git a/test-rig/src/main/resources/pack.mcmeta b/test-rig/src/main/resources/pack.mcmeta index 86489f25..40e941ce 100644 --- a/test-rig/src/main/resources/pack.mcmeta +++ b/test-rig/src/main/resources/pack.mcmeta @@ -1,8 +1,8 @@ { "pack": { - "description": "Advantage Works 1.18.2 test resources", - "pack_format": 9, - "forge:resource_pack_format": 8, - "forge:data_pack_format": 9 + "description": "Advantage Works 1.19.4 test resources", + "pack_format": 12, + "forge:resource_pack_format": 13, + "forge:data_pack_format": 12 } } diff --git a/test-rig/src/runner/java/zone/moddev/mc/advantageworks/runner/WorksRunner.java b/test-rig/src/runner/java/zone/moddev/mc/advantageworks/runner/WorksRunner.java index 06680f15..66639fb9 100644 --- a/test-rig/src/runner/java/zone/moddev/mc/advantageworks/runner/WorksRunner.java +++ b/test-rig/src/runner/java/zone/moddev/mc/advantageworks/runner/WorksRunner.java @@ -67,7 +67,7 @@ private WorksRunner() { public static void main(String[] args) throws Exception { Arguments arguments = Arguments.parse(args); - Profile profile = readNative118Profile(arguments.profile); + Profile profile = readNative119Profile(arguments.profile); if (arguments.serverPort != null) profile.serverPort = arguments.serverPort; File testRig = findTestRig(arguments.profile); File powerRoot = testRig.getParentFile().getCanonicalFile(); @@ -316,16 +316,17 @@ static void requireModularLauncher(File runtime, String launcher) throws Excepti Path args = root.resolve(launcher); PreparedServerSeed.requirePlain(args); boolean native118 = launcher.startsWith("libraries/net/minecraftforge/forge/1.18.2-40.3.0/"); + boolean native119 = launcher.startsWith("libraries/net/minecraftforge/forge/1.19.4-45.4.0/"); Set expected = new LinkedHashSet<>(Arrays.asList( "--add-modules ALL-MODULE-PATH", "--add-opens java.base/java.util.jar=cpw.mods.securejarhandler", "--add-exports java.base/sun.security.util=cpw.mods.securejarhandler", "-DlibraryDirectory=libraries", "cpw.mods.bootstraplauncher.BootstrapLauncher", - "--launchTarget forgeserver", native118 ? "--fml.forgeVersion 40.3.0" : "--fml.forgeVersion 37.1.1", - native118 ? "--fml.mcVersion 1.18.2" : "--fml.mcVersion 1.17.1", + "--launchTarget forgeserver", "--fml.forgeVersion " + (native119 ? "45.4.0" : native118 ? "40.3.0" : "37.1.1"), + "--fml.mcVersion " + (native119 ? "1.19.4" : native118 ? "1.18.2" : "1.17.1"), "--fml.forgeGroup net.minecraftforge", - native118 ? "--fml.mcpVersion 20220404.173914" : "--fml.mcpVersion 20210706.113038")); - if (native118) expected.addAll(Arrays.asList( + "--fml.mcpVersion " + (native119 ? "20230314.122934" : native118 ? "20220404.173914" : "20210706.113038"))); + if (native118 || native119) expected.addAll(Arrays.asList( "--add-opens java.base/java.lang.invoke=cpw.mods.securejarhandler", "--add-exports jdk.naming.dns/com.sun.jndi.dns=java.naming", "-Djava.net.preferIPv6Addresses=system")); @@ -353,7 +354,7 @@ static void requireModularLauncher(File runtime, String launcher) throws Excepti } } expected.addAll(Arrays.asList("module-path", "legacy-class-path", "ignore-list")); - if (!seen.equals(expected)) throw new IllegalArgumentException("Incomplete Forge 37 modular arguments"); + if (!seen.equals(expected)) throw new IllegalArgumentException("Incomplete Forge modular arguments"); } private static void archiveBoot(Profile profile, String phase) throws Exception { @@ -394,6 +395,8 @@ static final class BootLogs { "java.lang.UnsupportedOperationException: Reflective setAccessible(true) disabled", "jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable", "java.lang.IllegalAccessException: class io.netty.util.internal.PlatformDependent0$6 (in module io.netty.all) cannot access class jdk.internal.misc.Unsafe (in module java.base) because module java.base does not export jdk.internal.misc to module io.netty.all"); + private static final String NETTY_119_UNSAFE_PROBE = + "java.lang.IllegalAccessException: class io.netty.util.internal.PlatformDependent0$7 (in module io.netty.common) cannot access class jdk.internal.misc.Unsafe (in module java.base) because module java.base does not export jdk.internal.misc to module io.netty.common"; private static final Pattern NETTY_HEADER = Pattern.compile( "^\\[[^\\]]+\\] \\[[^\\]]+/DEBUG\\] " + Pattern.quote(NETTY_DEBUG) + "(.*)$"); private static final Pattern NETTY_FRAME = Pattern.compile( @@ -471,7 +474,8 @@ void archive() throws Exception { nettyFrames = false; continue; } - if (nettyProbe != null && line.equals(NETTY_PROBES.get(nettyProbe))) { + if (nettyProbe != null && (line.equals(NETTY_PROBES.get(nettyProbe)) + || (nettyProbe.startsWith("jdk.internal.misc.Unsafe") && line.equals(NETTY_119_UNSAFE_PROBE)))) { nonFatalDiagnostics.add(Map.of("line", line, "reason", "Netty DEBUG optional capability probe: " + nettyProbe)); nettyProbe = null; @@ -782,7 +786,9 @@ static PreparedServerSeed preparedServerSeed(File testRig, Profile profile, Argu } return new PreparedServerSeed(preparedPath(testRig, arguments.preparedServerSeed), preparedPath(testRig, arguments.preparedServerSeedInventory), arguments.preparedServerSeedSha256, - serverLauncherPath(profile), "1.18.2".equals(profile.minecraftVersion) + serverLauncherPath(profile), "1.19.4".equals(profile.minecraftVersion) + ? "libraries/net/minecraft/server/1.19.4-20230314.122934/server-1.19.4-20230314.122934-extra.jar" + : "1.18.2".equals(profile.minecraftVersion) ? "libraries/net/minecraft/server/1.18.2-20220404.173914/server-1.18.2-20220404.173914-extra.jar" : "1.17.1".equals(profile.minecraftVersion) ? "libraries/net/minecraft/server/1.17.1-20210706.113038/server-1.17.1-20210706.113038-extra.jar" @@ -1097,14 +1103,16 @@ static String serverLauncherPath(Profile profile) { } private static boolean modularForge(Profile profile) { - return "1.17.1".equals(profile.minecraftVersion) || "1.18.2".equals(profile.minecraftVersion); + return "1.17.1".equals(profile.minecraftVersion) || "1.18.2".equals(profile.minecraftVersion) + || "1.19.4".equals(profile.minecraftVersion); } private static File runtimeJavaHome(Profile profile) throws Exception { if (!modularForge(profile)) return java8Home(profile); - boolean native118 = "1.18.2".equals(profile.minecraftVersion); - String variable = native118 ? "ADVANTAGE_WORKS_JAVA17_HOME" : "ADVANTAGE_WORKS_JAVA16_HOME"; - String configured = native118 ? profile.java17Home : profile.java16Home; + boolean native119 = "1.19.4".equals(profile.minecraftVersion); + boolean java17 = native119 || "1.18.2".equals(profile.minecraftVersion); + String variable = java17 ? "ADVANTAGE_WORKS_JAVA17_HOME" : "ADVANTAGE_WORKS_JAVA16_HOME"; + String configured = java17 ? profile.java17Home : profile.java16Home; if (configured == null || configured.trim().isEmpty()) configured = System.getenv(variable); File home = configured == null || configured.trim().isEmpty() ? new File(System.getProperty("java.home")) : new File(expand(configured)); @@ -1113,8 +1121,8 @@ private static File runtimeJavaHome(Profile profile) throws Exception { if (release.isFile()) { try (InputStream input = Files.newInputStream(release.toPath())) { metadata.load(input); } } - String javaVersion = native118 ? "17.0.20.1" : "16.0.2"; - String temurin = native118 ? "17.0.20.1+1" : "16.0.2+7"; + String javaVersion = native119 ? "17.0.1" : java17 ? "17.0.20.1" : "16.0.2"; + String temurin = native119 ? "17.0.1+12" : java17 ? "17.0.20.1+1" : "16.0.2+7"; if (!javaExecutable(home).isFile() || !("\"" + javaVersion + "\"").equals(metadata.getProperty("JAVA_VERSION")) || !("\"Temurin-" + temurin + "\"").equals(metadata.getProperty("IMPLEMENTOR_VERSION"))) { throw new IllegalStateException("Set " + variable + " to the pinned Temurin " + temurin + " runtime: " + home); @@ -1263,14 +1271,25 @@ static Profile readNative117Profile(File file) throws Exception { return profile; } - static Profile readNative118Profile(File file) throws Exception { - Profile profile = readProfile(file, "1.18.2", "1.18.2-40.3.0"); + static Profile readNative119Profile(File file) throws Exception { + Profile profile = readProfile(file, "1.19.4", "1.19.4-45.4.0"); if (!"UNQUALIFIED".equals(profile.qualificationStatus)) { - throw new IllegalArgumentException("The 1.18.2 destination must start UNQUALIFIED"); + throw new IllegalArgumentException("The 1.19.4 destination must start UNQUALIFIED"); } for (ModInput input : profile.mods) { - if (input.version == null || !input.version.endsWith(".118021")) { - throw new IllegalArgumentException("Non-1.18.2 destination mod version: " + input.id); + if (input.version == null || !input.version.endsWith(".119041")) { + throw new IllegalArgumentException("Non-1.19.4 destination mod version: " + input.id); + } + } + if ("minecraft:flat".equals(profile.levelType)) { + if (!profile.generatorSettings.isEmpty()) { + throw new IllegalArgumentException("Works flat profiles must use the shared generator settings"); + } + try (InputStream input = WorksRunner.class.getResourceAsStream( + "/assets/advantageworkstest/works/flat-generator.json")) { + if (input == null) throw new IllegalStateException("Missing shared Works generator settings"); + var settings = GSON.fromJson(new InputStreamReader(input, StandardCharsets.UTF_8), JsonObject.class); + profile.generatorSettings = GSON.toJson(settings); } } return profile; diff --git a/test-rig/src/test/java/zone/moddev/mc/advantageworks/ConnectedProbeObservationTest.java b/test-rig/src/test/java/zone/moddev/mc/advantageworks/ConnectedProbeObservationTest.java index 48eebe07..6d081942 100644 --- a/test-rig/src/test/java/zone/moddev/mc/advantageworks/ConnectedProbeObservationTest.java +++ b/test-rig/src/test/java/zone/moddev/mc/advantageworks/ConnectedProbeObservationTest.java @@ -44,7 +44,7 @@ public class ConnectedProbeObservationTest { private static void observe(CompoundTag data) throws Exception { ObservedChest tile = new ObservedChest(); - Field persistent = BlockEntity.class.getDeclaredField("customTileData"); + Field persistent = BlockEntity.class.getDeclaredField("customPersistentData"); persistent.setAccessible(true); persistent.set(tile, data); CompoundTag before = tile.saveWithFullMetadata().copy(); @@ -84,12 +84,12 @@ private static T allocate(Class type) throws Exception { private static final class ObservedChest extends ChestBlockEntity { int persistentReads; ObservedChest() { super(POS, Blocks.CHEST.defaultBlockState()); } - @Override public CompoundTag getTileData() { persistentReads++; return super.getTileData(); } + @Override public CompoundTag getPersistentData() { persistentReads++; return super.getPersistentData(); } } private static final class ObservationWorld extends ServerLevel { BlockEntity tile; - private ObservationWorld() { super(null, null, null, null, null, null, null, null, false, 0L, List.of(), false); } + private ObservationWorld() { super(null, null, null, null, null, null, null, false, 0L, List.of(), false); } @Override public BlockState getBlockState(BlockPos pos) { return Blocks.CHEST.defaultBlockState(); } @Override public BlockEntity getBlockEntity(BlockPos pos) { return tile; } } diff --git a/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native117EntityContextTest.java b/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native117EntityContextTest.java index ec60aece..5226f3e8 100644 --- a/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native117EntityContextTest.java +++ b/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native117EntityContextTest.java @@ -84,12 +84,13 @@ private static void reject(Boat entity, ServerLevel world) { } static ServerLevel world(ResourceKey key) throws Exception { + zone.moddev.mc.advantageworks.unit.ForgeUnitRegistries.registerVanillaFluidTypes(); Class unsafe = Class.forName("sun.misc.Unsafe"); Field field = unsafe.getDeclaredField("theUnsafe"); field.setAccessible(true); ServerLevel world = (ServerLevel) unsafe.getMethod("allocateInstance", Class.class) - .invoke(field.get(null), ServerLevel.class); + .invoke(field.get(null), EntityFixtureWorld.class); set(world, "dimension", key); - set(world, "random", new java.util.Random(0)); + set(world, "random", net.minecraft.util.RandomSource.create(0)); set(world, "profiler", (Supplier) () -> InactiveProfiler.INSTANCE); Field ticks = ServerLevel.class.getDeclaredField("entityTickList"); ticks.setAccessible(true); ticks.set(world, new net.minecraft.world.level.entity.EntityTickList()); @@ -99,4 +100,17 @@ static ServerLevel world(ResourceKey key) throws Exception { private static void set(ServerLevel world, String name, Object value) throws Exception { Field field = Level.class.getDeclaredField(name); field.setAccessible(true); field.set(world, value); } + + private static final class EntityFixtureWorld extends ServerLevel { + private EntityFixtureWorld() { super(null, null, null, null, null, null, null, false, 0L, java.util.List.of(), false); } + + @Override public net.minecraft.world.flag.FeatureFlagSet enabledFeatures() { + return net.minecraft.world.flag.FeatureFlags.DEFAULT_FLAGS; + } + + @Override public void gameEvent(net.minecraft.world.level.gameevent.GameEvent event, + net.minecraft.world.phys.Vec3 pos, net.minecraft.world.level.gameevent.GameEvent.Context context) { + // This isolated entity fixture has no chunks or game-event listeners. + } + } } diff --git a/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native117JoiningHoldTest.java b/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native117JoiningHoldTest.java index 330a5636..fed8aa5b 100644 --- a/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native117JoiningHoldTest.java +++ b/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native117JoiningHoldTest.java @@ -9,7 +9,7 @@ import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.vehicle.Boat; import net.minecraft.world.level.Level; -import net.minecraftforge.event.entity.EntityJoinWorldEvent; +import net.minecraftforge.event.entity.EntityJoinLevelEvent; import org.junit.BeforeClass; import org.junit.Test; @@ -24,9 +24,9 @@ public class Native117JoiningHoldTest { boat.tickCount = 17; UUID identity = boat.getUUID(); MachineOperationalProbe probe = new MachineOperationalProbe(); - Method joining = MachineOperationalProbe.class.getDeclaredMethod("entityJoining", EntityJoinWorldEvent.class); + Method joining = MachineOperationalProbe.class.getDeclaredMethod("entityJoining", EntityJoinLevelEvent.class); joining.setAccessible(true); - joining.invoke(probe, new EntityJoinWorldEvent(boat, world)); + joining.invoke(probe, new EntityJoinLevelEvent(boat, world)); assertFalse(boat.canUpdate()); // Repeated START enumeration must not replace the pre-join state. Field field = MachineOperationalProbe.class.getDeclaredField("heldEntities"); field.setAccessible(true); @@ -46,9 +46,9 @@ public class Native117JoiningHoldTest { MachineOperationalProbe probe = new MachineOperationalProbe(); Field paused = MachineOperationalProbe.class.getDeclaredField("paused"); paused.setAccessible(true); paused.setBoolean(probe, false); - Method joining = MachineOperationalProbe.class.getDeclaredMethod("entityJoining", EntityJoinWorldEvent.class); + Method joining = MachineOperationalProbe.class.getDeclaredMethod("entityJoining", EntityJoinLevelEvent.class); joining.setAccessible(true); - joining.invoke(probe, new EntityJoinWorldEvent(boat, world)); + joining.invoke(probe, new EntityJoinLevelEvent(boat, world)); assertTrue(boat.canUpdate()); } diff --git a/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native117PlayerIoTest.java b/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native117PlayerIoTest.java index f72b9efc..4aed260a 100644 --- a/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native117PlayerIoTest.java +++ b/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native117PlayerIoTest.java @@ -107,7 +107,7 @@ public class Native117PlayerIoTest { } private static void assertSaved(CompoundTag saved, HeadlessPlayer player, int count, int slot) throws Exception { - assertEquals(SharedConstants.getCurrentVersion().getWorldVersion(), saved.getInt("DataVersion")); + assertEquals(SharedConstants.getCurrentVersion().getDataVersion().getVersion(), saved.getInt("DataVersion")); assertEquals(ID, saved.getUUID("UUID")); assertFalse(saved.contains("UUIDMost")); assertFalse(saved.contains("UUIDLeast")); @@ -187,7 +187,7 @@ private static final class HeadlessPlayer extends ServerPlayer { Inventory items; private HeadlessPlayer() { super(null, null, new GameProfile(ID, "Headless")); } @Override public CompoundTag saveWithoutId(CompoundTag root) { - root.putInt("DataVersion", SharedConstants.getCurrentVersion().getWorldVersion()); + root.putInt("DataVersion", SharedConstants.getCurrentVersion().getDataVersion().getVersion()); root.putUUID("UUID", getUUID()); root.put("Inventory", items.save(new ListTag())); return root; diff --git a/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native118TestFlatTest.java b/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native118TestFlatTest.java deleted file mode 100644 index 1b6d8d6f..00000000 --- a/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native118TestFlatTest.java +++ /dev/null @@ -1,67 +0,0 @@ -package zone.moddev.mc.advantageworks; - -import com.google.gson.JsonObject; -import com.mojang.serialization.JsonOps; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.server.Bootstrap; -import net.minecraft.core.RegistryAccess; -import net.minecraft.resources.RegistryOps; -import net.minecraft.world.level.levelgen.FlatLevelSource; -import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; -import net.minecraft.world.level.levelgen.WorldGenSettings; -import org.junit.BeforeClass; -import org.junit.Test; -import static org.junit.Assert.*; - -public class Native118TestFlatTest { - @BeforeClass public static void bootstrap() { net.minecraft.SharedConstants.tryDetectVersion(); Bootstrap.bootStrap(); } - - @Test public void constructorHasExactLayersAndNoStructures() { - RegistryAccess registries = RegistryAccess.builtinCopy().freeze(); - FlatLevelSource generator = (FlatLevelSource) Native118TestFlat.createType() - .createChunkGenerator(registries, 42L, ""); - FlatLevelGeneratorSettings flat = generator.settings(); - assertEquals(3, flat.getLayersInfo().size()); - java.util.List layers = flat.getLayers(); - assertEquals(4, layers.size()); - assertEquals(Blocks.BEDROCK.defaultBlockState(), layers.get(0)); - assertEquals(Blocks.DIRT.defaultBlockState(), layers.get(1)); - assertEquals(Blocks.DIRT.defaultBlockState(), layers.get(2)); - assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), layers.get(3)); - assertEquals(0, flat.structureOverrides().orElseThrow().size()); - } - - @Test public void fullSaveCodecRetainsVanillaFlatIdentityAndAllDimensions() { - RegistryAccess registries = RegistryAccess.builtinCopy().freeze(); - WorldGenSettings settings = Native118TestFlat.createType().createSettings(registries, 42L, false, false, ""); - JsonObject saved = WorldGenSettings.CODEC.encodeStart(RegistryOps.create(JsonOps.INSTANCE, registries), settings) - .result().orElseThrow(() -> new AssertionError("Native settings codec did not encode")).getAsJsonObject(); - assertEquals(42L, saved.get("seed").getAsLong()); - assertFalse(saved.get("generate_features").getAsBoolean()); - assertFalse(saved.get("bonus_chest").getAsBoolean()); - JsonObject dimensions = saved.getAsJsonObject("dimensions"); - assertEquals(3, dimensions.entrySet().size()); - assertTrue(dimensions.has("minecraft:the_nether")); - assertTrue(dimensions.has("minecraft:the_end")); - JsonObject generator = dimensions.getAsJsonObject("minecraft:overworld").getAsJsonObject("generator"); - assertEquals("minecraft:flat", generator.get("type").getAsString()); - JsonObject flat = generator.getAsJsonObject("settings"); - assertEquals("minecraft:plains", flat.get("biome").getAsString()); - assertEquals(3, flat.getAsJsonArray("layers").size()); - assertEquals(0, flat.getAsJsonArray("structure_overrides").size()); - assertFalse(saved.toString().contains(Native118TestFlat.ID)); - } - - @Test public void dedicatedServerPresetIdIsAllowedButOverridesAreRefused() { - RegistryAccess registries = RegistryAccess.builtinCopy().freeze(); - assertTrue(Native118TestFlat.createType().createChunkGenerator( - registries, 42L, Native118TestFlat.ID) instanceof FlatLevelSource); - try { - Native118TestFlat.createType().createChunkGenerator(registries, 42L, "{\"biome\":\"minecraft:desert\"}"); - fail("Flat JSON must not enter the vanilla JsonOps path"); - } catch (IllegalArgumentException expected) { - assertTrue(expected.getMessage().contains("Unexpected settings")); - } - } -} diff --git a/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native118DefinitionTest.java b/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native119DefinitionTest.java similarity index 90% rename from test-rig/src/test/java/zone/moddev/mc/advantageworks/Native118DefinitionTest.java rename to test-rig/src/test/java/zone/moddev/mc/advantageworks/Native119DefinitionTest.java index 96ee7fc2..a6502b94 100644 --- a/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native118DefinitionTest.java +++ b/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native119DefinitionTest.java @@ -6,16 +6,16 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; -public class Native118DefinitionTest { +public class Native119DefinitionTest { @Test public void nativeTargetPreservesEverySourceStationAndDistrict() throws Exception { Gson gson = new Gson(); try (InputStreamReader reader = new InputStreamReader(getClass().getResourceAsStream( "/assets/advantageworkstest/works/works-1.13.2.json"), StandardCharsets.UTF_8)) { WorksDefinition source = gson.fromJson(reader, WorksDefinition.class); - WorksDefinition target = WorksDefinitionLoader.loadNative118(); + WorksDefinition target = WorksDefinitionLoader.loadNative119(); assertEquals("1.13.2", source.minecraft); - assertEquals("1.18.2", target.minecraft); + assertEquals("1.19.4", target.minecraft); assertEquals(21, target.stations.size()); assertEquals(gson.toJsonTree(source.districts), gson.toJsonTree(target.districts)); assertEquals(gson.toJsonTree(source.stations), gson.toJsonTree(target.stations)); diff --git a/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native118PackMetadataTest.java b/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native119PackMetadataTest.java similarity index 73% rename from test-rig/src/test/java/zone/moddev/mc/advantageworks/Native118PackMetadataTest.java rename to test-rig/src/test/java/zone/moddev/mc/advantageworks/Native119PackMetadataTest.java index e9ec3db8..3328a890 100644 --- a/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native118PackMetadataTest.java +++ b/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native119PackMetadataTest.java @@ -8,16 +8,16 @@ import org.junit.Test; import static org.junit.Assert.*; -public class Native118PackMetadataTest { +public class Native119PackMetadataTest { @Test public void harnessResourcesDeclareTheNative118PackFormats() throws Exception { try (InputStream input = getClass().getResourceAsStream("/pack.mcmeta")) { assertNotNull(input); JsonObject pack = new JsonParser().parse(new InputStreamReader(input, StandardCharsets.UTF_8)) .getAsJsonObject().getAsJsonObject("pack"); assertTrue(pack.getAsJsonPrimitive("pack_format").isNumber()); - 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()); + assertEquals(12, pack.get("pack_format").getAsInt()); + assertEquals(13, pack.get("forge:resource_pack_format").getAsInt()); + assertEquals(12, pack.get("forge:data_pack_format").getAsInt()); } } } diff --git a/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native119TestFlatTest.java b/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native119TestFlatTest.java new file mode 100644 index 00000000..fd598e8e --- /dev/null +++ b/test-rig/src/test/java/zone/moddev/mc/advantageworks/Native119TestFlatTest.java @@ -0,0 +1,74 @@ +package zone.moddev.mc.advantageworks; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.mojang.serialization.JsonOps; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.List; +import net.minecraft.core.RegistryAccess; +import net.minecraft.resources.RegistryDataLoader; +import net.minecraft.resources.RegistryOps; +import net.minecraft.server.Bootstrap; +import net.minecraft.server.RegistryLayer; +import net.minecraft.server.packs.PackType; +import net.minecraft.server.packs.repository.ServerPacksSource; +import net.minecraft.server.packs.resources.MultiPackResourceManager; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.levelgen.FlatLevelSource; +import net.minecraft.world.level.levelgen.WorldDimensions; +import net.minecraft.world.level.levelgen.WorldGenSettings; +import net.minecraft.world.level.levelgen.WorldOptions; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; + +public class Native119TestFlatTest { + private static RegistryAccess registries; + + @BeforeClass public static void bootstrap() { + net.minecraft.SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + var base = RegistryLayer.createRegistryAccess(); + try (var resources = new MultiPackResourceManager(PackType.SERVER_DATA, + List.of(ServerPacksSource.createVanillaPackSource()))) { + var worldgen = RegistryDataLoader.load(resources, base.compositeAccess(), + RegistryDataLoader.WORLDGEN_REGISTRIES); + registries = base.replaceFrom(RegistryLayer.WORLDGEN, worldgen).compositeAccess(); + } + } + + @Test public void dedicatedServerSettingsKeepExactLayersAndSaveAllThreeDimensions() throws Exception { + JsonObject json; + try (var input = getClass().getResourceAsStream("/assets/advantageworkstest/works/flat-generator.json")) { + assertNotNull("Missing shared flat settings", input); + json = JsonParser.parseReader(new InputStreamReader(input, StandardCharsets.UTF_8)).getAsJsonObject(); + } + Class type = Class.forName("net.minecraft.server.dedicated.DedicatedServerProperties$WorldDimensionData"); + var constructor = type.getDeclaredConstructor(JsonObject.class, String.class); + constructor.setAccessible(true); + var create = type.getDeclaredMethod("create", RegistryAccess.class); + create.setAccessible(true); + var dimensions = (WorldDimensions) create.invoke(constructor.newInstance(json, "minecraft:flat"), registries); + var generator = (FlatLevelSource) dimensions.overworld(); + var flat = generator.settings(); + assertEquals(3, flat.getLayersInfo().size()); + assertEquals(List.of(Blocks.BEDROCK.defaultBlockState(), Blocks.DIRT.defaultBlockState(), + Blocks.DIRT.defaultBlockState(), Blocks.GRASS_BLOCK.defaultBlockState()), flat.getLayers()); + assertEquals(0, flat.structureOverrides().orElseThrow().size()); + + JsonObject saved = WorldGenSettings.CODEC.encodeStart(RegistryOps.create(JsonOps.INSTANCE, registries), + new WorldGenSettings(new WorldOptions(42L, false, false), dimensions)) + .getOrThrow(false, message -> { throw new AssertionError(message); }).getAsJsonObject(); + assertEquals(42L, saved.get("seed").getAsLong()); + assertFalse(saved.get("generate_features").getAsBoolean()); + assertFalse(saved.get("bonus_chest").getAsBoolean()); + JsonObject dimensionsTag = saved.getAsJsonObject("dimensions"); + assertEquals(3, dimensionsTag.entrySet().size()); + assertTrue(dimensionsTag.has("minecraft:the_nether")); + assertTrue(dimensionsTag.has("minecraft:the_end")); + JsonObject generatorTag = dimensionsTag.getAsJsonObject("minecraft:overworld").getAsJsonObject("generator"); + assertEquals("minecraft:flat", generatorTag.get("type").getAsString()); + assertEquals("minecraft:plains", generatorTag.getAsJsonObject("settings").get("biome").getAsString()); + } +} diff --git a/test-rig/unit-bootstrap/ForgeCapabilityBootstrap.java b/test-rig/unit-bootstrap/ForgeCapabilityBootstrap.java index 5c736fb3..fd8b8375 100644 --- a/test-rig/unit-bootstrap/ForgeCapabilityBootstrap.java +++ b/test-rig/unit-bootstrap/ForgeCapabilityBootstrap.java @@ -4,39 +4,49 @@ import java.lang.instrument.Instrumentation; import java.security.ProtectionDomain; -/** Runs Forge's own token transform for plain JUnit, without replacing its capabilities. */ +/** Runs Forge's own token and event transforms for plain JUnit. */ public final class ForgeCapabilityBootstrap { public static void premain(String options, Instrumentation instrumentation) { instrumentation.addTransformer(new ClassFileTransformer() { @Override public byte[] transform(ClassLoader loader, String name, Class type, ProtectionDomain domain, byte[] bytes) { - if (loader == null || name == null || !(name.contains("$") - || name.equals("net/minecraftforge/common/capabilities/CapabilityToken"))) return null; + if (loader == null || name == null) return null; + boolean event = name.startsWith("net/minecraftforge/network/NetworkEvent") + || name.equals("net/minecraftforge/registries/MissingMappingsEvent"); + String token = "net/minecraftforge/common/capabilities/CapabilityToken"; + if (!event && !(name.contains("$") || name.equals(token))) return null; try { Class readerType = Class.forName("org.objectweb.asm.ClassReader", true, loader); Object reader = readerType.getConstructor(byte[].class).newInstance((Object)bytes); String parent = (String)readerType.getMethod("getSuperName").invoke(reader); - String token = "net/minecraftforge/common/capabilities/CapabilityToken"; - if (!token.equals(name) && !token.equals(parent)) return null; + if (!event && !token.equals(name) && !token.equals(parent)) return null; Class nodeType = Class.forName("org.objectweb.asm.tree.ClassNode", true, loader); Object node = nodeType.getConstructor().newInstance(); Class visitorType = Class.forName("org.objectweb.asm.ClassVisitor", true, loader); readerType.getMethod("accept", visitorType, int.class).invoke(reader, node, 0); Class asmType = Class.forName("org.objectweb.asm.Type", true, loader); Object asmName = asmType.getMethod("getObjectType", String.class).invoke(null, name); - Class phaseType = Class.forName( - "cpw.mods.modlauncher.serviceapi.ILaunchPluginService$Phase", true, loader); - Object phase = phaseType.getField("BEFORE").get(null); - Class pluginType = Class.forName( - "net.minecraftforge.fml.common.asm.CapabilityTokenSubclass", true, loader); - pluginType.getMethod("processClassWithFlags", phaseType, nodeType, asmType, String.class) - .invoke(pluginType.getConstructor().newInstance(), phase, node, asmName, "junit"); + if (event) { + Class transformer = Class.forName("net.minecraftforge.eventbus.EventSubclassTransformer", true, loader); + var transformed = (java.util.Optional) transformer.getMethod("transform", nodeType, asmType) + .invoke(transformer.getConstructor().newInstance(), node, asmName); + if (transformed.isEmpty()) return null; + node = transformed.get(); + } else { + Class phaseType = Class.forName( + "cpw.mods.modlauncher.serviceapi.ILaunchPluginService$Phase", true, loader); + Object phase = phaseType.getField("BEFORE").get(null); + Class pluginType = Class.forName( + "net.minecraftforge.fml.common.asm.CapabilityTokenSubclass", true, loader); + pluginType.getMethod("processClassWithFlags", phaseType, nodeType, asmType, String.class) + .invoke(pluginType.getConstructor().newInstance(), phase, node, asmName, "junit"); + } Class writerType = Class.forName("org.objectweb.asm.ClassWriter", true, loader); Object writer = writerType.getConstructor(int.class).newInstance(1); nodeType.getMethod("accept", visitorType).invoke(node, writer); return (byte[])writerType.getMethod("toByteArray").invoke(writer); } catch (ReflectiveOperationException failure) { - throw new IllegalStateException("Cannot run Forge's native capability transform for " + name, failure); + throw new IllegalStateException("Cannot run Forge's native unit transform for " + name, failure); } } }); diff --git a/test-rig/unit-support/zone/moddev/mc/advantageworks/unit/ForgeUnitRegistries.java b/test-rig/unit-support/zone/moddev/mc/advantageworks/unit/ForgeUnitRegistries.java new file mode 100644 index 00000000..f23cf57b --- /dev/null +++ b/test-rig/unit-support/zone/moddev/mc/advantageworks/unit/ForgeUnitRegistries.java @@ -0,0 +1,45 @@ +package zone.moddev.mc.advantageworks.unit; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; +import net.minecraftforge.common.ForgeMod; +import net.minecraftforge.registries.DeferredRegister; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.ForgeRegistry; +import net.minecraftforge.registries.IForgeRegistry; +import net.minecraftforge.registries.NewRegistryEvent; +import net.minecraftforge.registries.RegisterEvent; + +/** Runs the narrow Forge registry phases needed by native test entities outside ModLauncher. */ +public final class ForgeUnitRegistries { + private ForgeUnitRegistries() {} + + public static synchronized void registerVanillaFluidTypes() throws ReflectiveOperationException { + if (ForgeRegistries.FLUID_TYPES.get() != null) return; + NewRegistryEvent event = new NewRegistryEvent(); + Field types = ForgeRegistries.class.getDeclaredField("DEFERRED_FLUID_TYPES"); + types.setAccessible(true); + Method create = DeferredRegister.class.getDeclaredMethod("createRegistry", NewRegistryEvent.class); + create.setAccessible(true); + create.invoke(types.get(null), event); + Method fill = NewRegistryEvent.class.getDeclaredMethod("fill"); + fill.setAccessible(true); + fill.invoke(event); + registerForgeEntries("VANILLA_FLUID_TYPES", ForgeRegistries.Keys.FLUID_TYPES, ForgeRegistries.FLUID_TYPES.get()); + } + + public static void registerForgeEntries(String name, ResourceKey key, IForgeRegistry registry) + throws ReflectiveOperationException { + Field field = ForgeMod.class.getDeclaredField(name); + field.setAccessible(true); + Method register = DeferredRegister.class.getDeclaredMethod("addEntries", RegisterEvent.class); + register.setAccessible(true); + Constructor ctor = RegisterEvent.class.getDeclaredConstructor( + ResourceKey.class, ForgeRegistry.class, Registry.class); + ctor.setAccessible(true); + register.invoke(field.get(null), ctor.newInstance(key, registry, null)); + } +}