From 708ed4e14347aff6fd7ebacaeb904faab1700694 Mon Sep 17 00:00:00 2001 From: JohnBraham Date: Wed, 11 Nov 2026 16:43:22 +0000 Subject: [PATCH 1/8] Port baseSteam to native Forge 61 for Minecraft 1.21.11 --- .github/verify-published-pins.sh | 10 +- .github/workflows/ci.yml | 26 +-- .github/workflows/codeql-analysis.yml | 12 +- .github/workflows/release-on-tag.yml | 6 +- .github/workflows/validate-gradle-build.yml | 4 +- CHANGELOG.txt | 12 +- README.md | 34 ++-- build.gradle | 124 ++++++++---- docs/FORGE61-RESOURCE-METADATA.md | 81 ++++++++ docs/PORT1211-STATUS.md | 14 +- docs/PORT12111-STATUS.md | 88 +++++++++ gradle.properties | 68 +++---- gradle/forge52-mavenizer.gradle | 25 --- gradle/forge61-mavenizer.gradle | 31 +++ .../gametest/SteamGameTestRegistration.java | 28 +++ .../gametest/SteamNativeGameTests.java | 61 +++--- ...preserves_type_position_and_direction.json | 7 + ..._byte_survive_native_componentization.json | 7 + ...tive_resources_load_with_expected_ids.json | 7 + ...op_level_wear_survive_power_migration.json | 7 + .../moddev/mc/basesteam/SteamAdvantage.java | 17 +- .../mc/basesteam/blocks/DrillBitBlock.java | 22 +-- .../basesteam/blocks/DrillBitTileEntity.java | 32 ++- .../mc/basesteam/blocks/PlatformBlock.java | 5 +- .../mc/basesteam/blocks/PumpPipeBlock.java | 13 +- .../blocks/SteamBlockProperties.java | 15 +- .../mc/basesteam/blocks/SteamPipeBlock.java | 2 +- .../mc/basesteam/blocks/SteamTrackBlock.java | 2 +- .../mc/basesteam/compat/BaseMetalsCompat.java | 6 +- .../graphics/ClientRegistration.java | 21 +- .../basesteam/graphics/DrillBitRenderer.java | 73 +++++-- .../mc/basesteam/gui/BlastFurnaceGUI.java | 6 +- .../mc/basesteam/gui/CoalBoilerGUI.java | 4 +- .../mc/basesteam/gui/ElectricBoilerGUI.java | 4 +- .../moddev/mc/basesteam/gui/GUIHelper.java | 90 ++------- .../mc/basesteam/gui/GeothermalBoilerGUI.java | 4 +- .../moddev/mc/basesteam/gui/OilBoilerGUI.java | 4 +- .../mc/basesteam/gui/RockCrusherGUI.java | 4 +- .../mc/basesteam/gui/SteamDrillGUI.java | 4 +- .../moddev/mc/basesteam/gui/SteamPumpGUI.java | 4 +- .../mc/basesteam/gui/SteamStillGUI.java | 4 +- .../moddev/mc/basesteam/gui/SteamTankGUI.java | 4 +- .../zone/moddev/mc/basesteam/init/Blocks.java | 15 +- .../mc/basesteam/init/Enchantments.java | 6 +- .../moddev/mc/basesteam/init/Entities.java | 8 +- .../zone/moddev/mc/basesteam/init/Items.java | 20 +- .../zone/moddev/mc/basesteam/init/Power.java | 6 +- .../moddev/mc/basesteam/init/Villages.java | 16 +- .../moddev/mc/basesteam/items/MusketItem.java | 91 ++++----- .../basesteam/machines/BlastFurnaceBlock.java | 2 +- .../machines/BlastFurnaceTileEntity.java | 24 +-- .../basesteam/machines/CoalBoilerBlock.java | 2 +- .../machines/CoalBoilerTileEntity.java | 33 ++-- .../machines/ElectricBoilerBlock.java | 2 +- .../machines/ElectricBoilerTileEntity.java | 23 +-- .../machines/GeothermalBoilerBlock.java | 2 +- .../machines/GeothermalBoilerTileEntity.java | 27 +-- .../mc/basesteam/machines/OilBoilerBlock.java | 2 +- .../machines/OilBoilerTileEntity.java | 35 ++-- .../basesteam/machines/RockCrusherBlock.java | 2 +- .../machines/RockCrusherTileEntity.java | 15 +- .../basesteam/machines/SteamDrillBlock.java | 22 +-- .../machines/SteamDrillTileEntity.java | 42 ++-- .../machines/SteamElevatorBlock.java | 2 +- .../machines/SteamElevatorTileEntity.java | 15 +- .../mc/basesteam/machines/SteamPumpBlock.java | 14 +- .../machines/SteamPumpTileEntity.java | 29 +-- .../basesteam/machines/SteamStillBlock.java | 2 +- .../machines/SteamStillTileEntity.java | 23 +-- .../mc/basesteam/machines/SteamTankBlock.java | 2 +- .../migration/SteamLegacyMigrations.java | 16 +- .../mc/basesteam/recipe/RecipeConditions.java | 4 +- .../mc/basesteam/util/FurnaceFuelHelper.java | 14 +- .../moddev/mc/basesteam/util/SoundHelper.java | 4 +- src/main/resources/META-INF/mods.toml | 3 +- .../items/blackpowder_cartridge.json | 6 + .../assets/basesteam/items/drillbit.json | 6 + .../assets/basesteam/items/musket.json | 24 +++ .../assets/basesteam/items/platform.json | 6 + .../basesteam/items/pump_pipe_steam.json | 6 + .../basesteam/items/steam_boiler_coal.json | 6 + .../items/steam_boiler_electric.json | 6 + .../items/steam_boiler_geothermal.json | 6 + .../basesteam/items/steam_boiler_oil.json | 6 + .../assets/basesteam/items/steam_crusher.json | 6 + .../assets/basesteam/items/steam_drill.json | 6 + .../basesteam/items/steam_drill_bit.json | 6 + .../basesteam/items/steam_elevator.json | 6 + .../assets/basesteam/items/steam_furnace.json | 6 + .../basesteam/items/steam_governor.json | 6 + .../assets/basesteam/items/steam_pipe.json | 6 + .../assets/basesteam/items/steam_pump.json | 6 + .../assets/basesteam/items/steam_still.json | 6 + .../assets/basesteam/items/steam_switch.json | 6 + .../assets/basesteam/items/steam_tank.json | 6 + .../assets/basesteam/items/steam_track.json | 6 + .../assets/basesteam/models/item/musket.json | 26 +-- .../assets/minecraft/atlases/items.json | 5 + .../recipe/blackpowder_cartridge.json | 12 +- .../recipe/crushing_steam_boiler_coal.json | 4 +- .../crushing_steam_boiler_electric.json | 4 +- .../crushing_steam_boiler_geothermal.json | 4 +- .../recipe/crushing_steam_crusher.json | 4 +- .../recipe/crushing_steam_drill.json | 4 +- .../recipe/crushing_steam_elevator.json | 4 +- .../recipe/crushing_steam_furnace.json | 4 +- .../recipe/crushing_steam_governor.json | 4 +- .../basesteam/recipe/crushing_steam_tank.json | 4 +- .../basesteam/recipe/musket_left_normal.json | 12 +- .../basesteam/recipe/musket_left_tech.json | 12 +- .../basesteam/recipe/musket_right_normal.json | 12 +- .../basesteam/recipe/musket_right_tech.json | 12 +- .../basesteam/recipe/steam_boiler_coal.json | 16 +- .../recipe/steam_boiler_electric.json | 20 +- .../recipe/steam_boiler_geothermal.json | 20 +- .../basesteam/recipe/steam_boiler_oil.json | 20 +- .../data/basesteam/recipe/steam_crusher.json | 20 +- .../data/basesteam/recipe/steam_drill.json | 16 +- .../basesteam/recipe/steam_drill_bit.json | 12 +- .../steam_drill_diamond_pickaxe_normal.json | 16 +- .../data/basesteam/recipe/steam_elevator.json | 20 +- .../data/basesteam/recipe/steam_furnace.json | 16 +- .../recipe/steam_governor_apocalyptic.json | 16 +- .../recipe/steam_governor_normal.json | 16 +- .../basesteam/recipe/steam_governor_tech.json | 16 +- .../data/basesteam/recipe/steam_pipe.json | 4 +- .../data/basesteam/recipe/steam_pump.json | 20 +- .../data/basesteam/recipe/steam_still.json | 20 +- .../data/basesteam/recipe/steam_switch.json | 12 +- .../data/basesteam/recipe/steam_tank.json | 12 +- .../data/basesteam/recipe/steam_track.json | 8 +- src/main/resources/pack.mcmeta | 4 +- .../blocks/DrillBitPersistenceTest.java | 23 ++- .../graphics/DrillBitRendererTest.java | 6 + .../mc/basesteam/gui/GUIHelperTest.java | 80 +++++++- .../items/MusketPersistenceTest.java | 11 +- .../items/MusketTargetingContractTest.java | 2 +- .../CoalBoilerFillPersistenceTest.java | 6 +- .../RockCrusherReloadRedstoneTest.java | 2 +- .../machines/SteamDrillPersistenceTest.java | 58 ++++-- .../machines/SteamMachinePersistenceTest.java | 49 +++-- .../SteamOperationalPumpSourceTest.java | 12 +- .../WaterBoilerTankNotificationTest.java | 10 +- .../migration/SteamLegacyMigrationsTest.java | 24 +-- .../resources/BuildWorkflowContractTest.java | 14 +- .../MusketFragileTagContractTest.java | 4 +- .../NativeBlockEntityContractTest.java | 9 +- .../resources/NativeBlockPropertiesTest.java | 7 +- .../NativeEnchantmentContractTest.java | 24 +-- .../resources/NativeFuelContractTest.java | 26 +-- .../NativeIngredientCodecContractTest.java | 92 +++++++++ .../NativeItemAtlasContractTest.java | 126 ++++++++++++ .../NativeItemDefinitionContractTest.java | 65 ++++++ .../resources/NativeLootContractTest.java | 10 +- .../NativePackFormatContractTest.java | 53 +++++ .../NativePackRangeContractTest.java | 56 ++++++ .../OptionalCrushingRecipeContractTest.java | 7 +- .../RecipeRegistrationThreadTest.java | 2 +- .../resources/RecipeUnlockContractTest.java | 28 ++- .../testing/EclipseUnitLauncher.java | 43 ++++ .../testing/NativeMachineFixture.java | 6 +- .../testing/NativePackResourceProof.java | 185 ++++++++++++++++++ .../testing/NativeTestBootstrap.java | 52 +++-- tools/generate-recipe-unlocks.js | 4 +- tools/run-packaged-server-smoke.ps1 | 12 +- tools/verify-eclipse-ecj.ps1 | 106 ++++++++++ tools/verify-native-port.js | 12 +- 167 files changed, 2208 insertions(+), 1108 deletions(-) create mode 100644 docs/FORGE61-RESOURCE-METADATA.md create mode 100644 docs/PORT12111-STATUS.md delete mode 100644 gradle/forge52-mavenizer.gradle create mode 100644 gradle/forge61-mavenizer.gradle create mode 100644 src/gametest/java/zone/moddev/mc/basesteam/gametest/SteamGameTestRegistration.java create mode 100644 src/gametest/resources/data/basesteam/test_instance/drill_bit_packet_preserves_type_position_and_direction.json create mode 100644 src/gametest/resources/data/basesteam/test_instance/legacy_musket_wear_and_loaded_byte_survive_native_componentization.json create mode 100644 src/gametest/resources/data/basesteam/test_instance/native_resources_load_with_expected_ids.json create mode 100644 src/gametest/resources/data/basesteam/test_instance/older_musket_alias_and_top_level_wear_survive_power_migration.json create mode 100644 src/main/resources/assets/basesteam/items/blackpowder_cartridge.json create mode 100644 src/main/resources/assets/basesteam/items/drillbit.json create mode 100644 src/main/resources/assets/basesteam/items/musket.json create mode 100644 src/main/resources/assets/basesteam/items/platform.json create mode 100644 src/main/resources/assets/basesteam/items/pump_pipe_steam.json create mode 100644 src/main/resources/assets/basesteam/items/steam_boiler_coal.json create mode 100644 src/main/resources/assets/basesteam/items/steam_boiler_electric.json create mode 100644 src/main/resources/assets/basesteam/items/steam_boiler_geothermal.json create mode 100644 src/main/resources/assets/basesteam/items/steam_boiler_oil.json create mode 100644 src/main/resources/assets/basesteam/items/steam_crusher.json create mode 100644 src/main/resources/assets/basesteam/items/steam_drill.json create mode 100644 src/main/resources/assets/basesteam/items/steam_drill_bit.json create mode 100644 src/main/resources/assets/basesteam/items/steam_elevator.json create mode 100644 src/main/resources/assets/basesteam/items/steam_furnace.json create mode 100644 src/main/resources/assets/basesteam/items/steam_governor.json create mode 100644 src/main/resources/assets/basesteam/items/steam_pipe.json create mode 100644 src/main/resources/assets/basesteam/items/steam_pump.json create mode 100644 src/main/resources/assets/basesteam/items/steam_still.json create mode 100644 src/main/resources/assets/basesteam/items/steam_switch.json create mode 100644 src/main/resources/assets/basesteam/items/steam_tank.json create mode 100644 src/main/resources/assets/basesteam/items/steam_track.json create mode 100644 src/main/resources/assets/minecraft/atlases/items.json create mode 100644 src/test/java/zone/moddev/mc/basesteam/resources/NativeIngredientCodecContractTest.java create mode 100644 src/test/java/zone/moddev/mc/basesteam/resources/NativeItemAtlasContractTest.java create mode 100644 src/test/java/zone/moddev/mc/basesteam/resources/NativeItemDefinitionContractTest.java create mode 100644 src/test/java/zone/moddev/mc/basesteam/resources/NativePackFormatContractTest.java create mode 100644 src/test/java/zone/moddev/mc/basesteam/resources/NativePackRangeContractTest.java create mode 100644 src/test/java/zone/moddev/mc/basesteam/testing/EclipseUnitLauncher.java create mode 100644 src/test/java/zone/moddev/mc/basesteam/testing/NativePackResourceProof.java create mode 100644 tools/verify-eclipse-ecj.ps1 diff --git a/.github/verify-published-pins.sh b/.github/verify-published-pins.sh index 4e2361e..8cd0265 100644 --- a/.github/verify-published-pins.sh +++ b/.github/verify-published-pins.sh @@ -9,10 +9,10 @@ require_merged() { echo "::error::$repo needs an exact source pin. A pending local candidate is not a public dependency." exit 1 fi - status="$(gh api "repos/MinecraftModDevelopmentMods/$repo/compare/$ref...master-1.21.1" --jq '.status')" + status="$(gh api "repos/MinecraftModDevelopmentMods/$repo/compare/$ref...master-1.21.11" --jq '.status')" case "$status" in identical|ahead) ;; - *) echo "::error::$repo $ref is not on upstream master-1.21.1 ($status)."; exit 1 ;; + *) echo "::error::$repo $ref is not on upstream master-1.21.11 ($status)."; exit 1 ;; esac } @@ -39,7 +39,7 @@ require_merged basePower "$power_ref" require_merged BaseMetals "$(value basemetals_source_commit)" require_merged OreSpawn "$orespawn_ref" -inputs="${RUNNER_TEMP:?}/forge52-release-dependencies" +inputs="${RUNNER_TEMP:?}/forge61-release-dependencies" mkdir -p "$inputs" power="$inputs/basePower-$(value poweradvantage_version).jar" metals="$inputs/BaseMetals-$(value basemetals_version).jar" @@ -48,8 +48,8 @@ public_jar basePower 1780140 "$(value poweradvantage_public_file_id)" "$(value p public_jar BaseMetals 240967 "$(value basemetals_public_file_id)" "$(value basemetals_deobf_sha256)" "$metals" public_jar OreSpawn 245586 "$(value orespawn_public_file_id)" "$(value orespawn_deobf_sha256)" "$orespawn" -installer="$inputs/forge-1.21.1-52.1.0-installer.jar" -download 'https://maven.minecraftforge.net/net/minecraftforge/forge/1.21.1-52.1.0/forge-1.21.1-52.1.0-installer.jar' \ +installer="$inputs/forge-1.21.11-61.1.0-installer.jar" +download 'https://maven.minecraftforge.net/net/minecraftforge/forge/1.21.11-61.1.0/forge-1.21.11-61.1.0-installer.jar' \ "$(value forge_installer_sha256)" "$installer" { printf 'power_ref=%s\n' "$power_ref" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 99c0d8f..77bccf0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,16 +1,16 @@ -name: baseSteam 1.21.1 CI +name: baseSteam 1.21.11 CI on: push: - branches: [master-1.21.1, 'feature/**'] + branches: [master-1.21.11, 'feature/**'] pull_request: - branches: [master-1.21.1] + branches: [master-1.21.11] permissions: contents: read concurrency: - group: basesteam-1.21.1-${{ github.workflow }}-${{ github.ref }} + group: basesteam-1.21.11-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: @@ -62,7 +62,7 @@ jobs: -Porg.gradle.java.installations.auto-download=false" >> "$GITHUB_ENV" - name: Set up Gradle uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6 - - name: Verify the Forge 52 build inputs + - name: Verify the Forge 61 build inputs shell: bash env: ORESPAWN_REF: ${{ steps.public-pins.outputs.orespawn_ref }} @@ -74,7 +74,7 @@ jobs: test "$(git -C "$orespawn" rev-parse HEAD)" = "$ORESPAWN_REF" test "$(git -C "$power" rev-parse HEAD)" = "$POWER_REF" mavenizer="$orespawn/ci-fixtures/tools/minecraft-mavenizer-0.5.21-orespawn-compat.jar" - printf '%s %s\n' '019032A4A26ECB473D403EF017054D5892CE59A9904E8831B452287CD756397E' "$mavenizer" | sha256sum -c - + printf '%s %s\n' 'B36CA046DB72E30F27BD38E02B4AE34274ADB51AE1D65BAF9E1D07763755682E' "$mavenizer" | sha256sum -c - echo "MAVENIZER=$mavenizer" >> "$GITHUB_ENV" - name: Build the released basePower test bootstrap shell: bash @@ -82,7 +82,7 @@ jobs: set -euo pipefail power="$GITHUB_WORKSPACE/.ci-dependencies/basePower" (cd "$power" && bash ./gradlew $MMD_GRADLE_TOOLCHAINS unitBootstrapJar \ - "-Pforge52MavenizerJar=$MAVENIZER" "-PbaseMetalsDeobfJar=$METALS_PLAYER" "-PoreSpawnDeobfJar=$ORESPAWN_PLAYER" --no-daemon) + "-Pforge61MavenizerJar=$MAVENIZER" "-PbaseMetalsDeobfJar=$METALS_PLAYER" "-PoreSpawnDeobfJar=$ORESPAWN_PLAYER" --no-daemon) echo "POWER_BOOTSTRAP=$power/build/unit-bootstrap/forge-native-unit-bootstrap.jar" >> "$GITHUB_ENV" echo "POWER_BOOTSTRAP_SOURCE=$power/test-rig/unit-bootstrap/ForgeCapabilityBootstrap.java" >> "$GITHUB_ENV" echo "POWER_REGISTRY_SOURCE=$power/test-rig/unit-support/zone/moddev/mc/advantageworks/unit/ForgeUnitRegistries.java" >> "$GITHUB_ENV" @@ -91,27 +91,27 @@ jobs: run: | ./gradlew $MMD_GRADLE_TOOLCHAINS clean check build javadoc verifyReleaseDependencies verifyReleaseArtifacts \ writeReleaseChecksums verifyEclipseProductionClasspath --no-build-cache --no-daemon --stacktrace \ - "-Pforge52MavenizerJar=$MAVENIZER" "-PpowerAdvantageDeobfJar=$POWER_PLAYER" \ + "-Pforge61MavenizerJar=$MAVENIZER" "-PpowerAdvantageDeobfJar=$POWER_PLAYER" \ "-PbaseMetalsDeobfJar=$METALS_PLAYER" "-PoreSpawnDeobfJar=$ORESPAWN_PLAYER" \ "-PforgeCapabilityBootstrapJar=$POWER_BOOTSTRAP" "-PforgeCapabilityBootstrapSource=$POWER_BOOTSTRAP_SOURCE" \ "-PforgeUnitRegistriesSource=$POWER_REGISTRY_SOURCE" - name: Upload audited release candidate uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: - name: baseSteam-1.21.1-${{ github.sha }} + name: baseSteam-1.21.11-${{ github.sha }} if-no-files-found: error retention-days: 30 path: | - build/libs/baseSteam-2.2.15.121011.jar - build/libs/baseSteam-2.2.15.121011-sources.jar - build/libs/baseSteam-2.2.15.121011-javadoc.jar + build/libs/baseSteam-2.2.16.121111.jar + build/libs/baseSteam-2.2.16.121111-sources.jar + build/libs/baseSteam-2.2.16.121111-javadoc.jar build/release/SHA256SUMS CHANGELOG.txt - name: Upload diagnostics on failure if: failure() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: - name: baseSteam-1.21.1-diagnostics-${{ github.sha }} + name: baseSteam-1.21.11-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 f3faaa9..2ebea56 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -2,9 +2,9 @@ name: CodeQL on: push: - branches: [master-1.21.1, 'feature/**'] + branches: [master-1.21.11, 'feature/**'] pull_request: - branches: [master-1.21.1] + branches: [master-1.21.11] permissions: contents: read @@ -12,7 +12,7 @@ permissions: security-events: write concurrency: - group: basesteam-1.21.1-${{ github.workflow }}-${{ github.ref }} + group: basesteam-1.21.11-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: @@ -64,7 +64,7 @@ jobs: -Porg.gradle.java.installations.auto-download=false" >> "$GITHUB_ENV" - name: Set up Gradle uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6 - - name: Verify the Forge 52 build inputs + - name: Verify the Forge 61 build inputs shell: bash env: ORESPAWN_REF: ${{ steps.public-pins.outputs.orespawn_ref }} @@ -76,7 +76,7 @@ jobs: test "$(git -C "$orespawn" rev-parse HEAD)" = "$ORESPAWN_REF" test "$(git -C "$power" rev-parse HEAD)" = "$POWER_REF" mavenizer="$orespawn/ci-fixtures/tools/minecraft-mavenizer-0.5.21-orespawn-compat.jar" - printf '%s %s\n' '019032A4A26ECB473D403EF017054D5892CE59A9904E8831B452287CD756397E' "$mavenizer" | sha256sum -c - + printf '%s %s\n' 'B36CA046DB72E30F27BD38E02B4AE34274ADB51AE1D65BAF9E1D07763755682E' "$mavenizer" | sha256sum -c - echo "MAVENIZER=$mavenizer" >> "$GITHUB_ENV" - name: Initialize CodeQL uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4 @@ -87,7 +87,7 @@ jobs: shell: bash run: | ./gradlew $MMD_GRADLE_TOOLCHAINS classes verifyReleaseDependencies --rerun-tasks --no-build-cache --no-daemon --stacktrace \ - "-Pforge52MavenizerJar=$MAVENIZER" "-PpowerAdvantageDeobfJar=$POWER_PLAYER" \ + "-Pforge61MavenizerJar=$MAVENIZER" "-PpowerAdvantageDeobfJar=$POWER_PLAYER" \ "-PbaseMetalsDeobfJar=$METALS_PLAYER" "-PoreSpawnDeobfJar=$ORESPAWN_PLAYER" - name: Analyze uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4 diff --git a/.github/workflows/release-on-tag.yml b/.github/workflows/release-on-tag.yml index 94ce821..9a26df1 100644 --- a/.github/workflows/release-on-tag.yml +++ b/.github/workflows/release-on-tag.yml @@ -37,9 +37,9 @@ jobs: minecraft_version="$(value minecraft_version)" loader_name="$(value loader_name)" loader_code="$(value loader_code)" - if [[ "$minecraft_version" != '1.21.1' || "$(value forge_version)" != '52.1.0' || - "$(value mapping_version)" != '1.21.1' || "$(value mcp_version)" != '20240808.132146' ]]; then - echo 'Expected the exact Minecraft 1.21.1 Forge 52.1.0 release target' >&2 + if [[ "$minecraft_version" != '1.21.11' || "$(value forge_version)" != '61.1.0' || + "$(value mapping_version)" != '1.21.11' || "$(value mcp_version)" != '20251223.124241' ]]; then + echo 'Expected the exact Minecraft 1.21.11 Forge 61.1.0 release target' >&2 exit 1 fi diff --git a/.github/workflows/validate-gradle-build.yml b/.github/workflows/validate-gradle-build.yml index 2f2c732..b37dfc9 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.21.1 + - master-1.21.11 - 'feature/**' pull_request: branches: - - master-1.21.1 + - master-1.21.11 permissions: contents: read diff --git a/CHANGELOG.txt b/CHANGELOG.txt index cde438f..57dae58 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,8 +1,18 @@ -baseSteam 2.2.15.121011 (local Minecraft 1.21.1 candidate) +baseSteam 2.2.16.121111 (local Minecraft 1.21.11 source port) + +- Target Forge 61.1.0, Java 21 and the pinned local basePower 2.4.15.121111 API. +- Adapt native registration, persistence, recipes, item definitions and drill rendering. +- Retain registry identities, legacy state keys, recipe payloads and configuration defaults. +- Use native Forge hidden-client pack metadata with data-format 94.1 at the mod root. +- Stitch the drill-bit sprite into the native item atlas as well as the block atlas. +- Local source and archive gates pass; runtime and combined upgrade qualification remains open. + +baseSteam 2.2.15.121011 (Minecraft 1.21.1 / Forge) - Target Forge 52.1.0 and Java 21 with basePower 2.4.14.121011. - Keep machine balance, saved state, recipes and the whole fluid gauges unchanged. - Move musket enchantments to native data while retaining all four IDs and effects. +- Pin the released basePower, Base Metals and OreSpawn jars used for the upgrade tests. baseSteam 2.2.14.120061 (Minecraft 1.20.6 / Forge) diff --git a/README.md b/README.md index ba7f0ea..8b75717 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # baseSteam -**Minecraft 1.21.1 (Forge), local candidate 2.2.15.121011.** +**Minecraft 1.21.11 (Forge), local source port 2.2.16.121111.** baseSteam adds boilers, steam-powered machines, pumps, storage, elevators and a blackpowder musket to basePower. Items and blocks share the basePower @@ -8,20 +8,26 @@ creative tab. The mod ID and resource namespace remain `basesteam`. ## Build -Target: Forge 52.1.0, official Minecraft 1.21.1 mappings and MCP -20240808.132146. ForgeGradle 7.0.34 and the checksum-pinned Gradle 9.6.1 +Target: Forge 61.1.0, official Minecraft 1.21.11 mappings and MCP +20251223.124241. ForgeGradle 7.0.34 and the checksum-pinned Gradle 9.6.1 wrapper use Temurin 21.0.7+6. Mavenizer uses Java 25; the launcher helper also needs Java 8. Minecraft itself runs on Java 21. -The default dependency paths use the coordinated 1.21.1 checkouts. This -candidate needs basePower 2.4.14.121011, Base Metals 3.1.2.121011 and -OreSpawn 4.1.0.121011 as separate native jars, not bundled copies. +The default dependency paths use the coordinated 1.21.11 checkouts. This +candidate needs basePower 2.4.15.121111, Base Metals 3.1.2.121111 and +OreSpawn 4.1.0.121111 as separate native jars, not bundled copies. Exact local source, jar and test-bootstrap pins are in `gradle.properties`. They are checked before compilation and tests. Local approval does not -establish public availability. +establish public availability. The Base Metals shared hook and approved Power +integration commit 398439074a60bf9b8f8872f92485cffc4aa96be9 are pinned locally. +The mod root declares server data format 94.1. Forge supplies format 75.x +metadata for its hidden client resource aggregate. Native resource-reader +tests cover both paths without codec warnings; see +[Forge metadata notes](docs/FORGE61-RESOURCE-METADATA.md) for the separate +Forge 61.1.0 server compatibility-label defect. -The Mavenizer fixture comes from OreSpawn's OS1211 checkout. Another layout -can use `-Pforge52MavenizerJar=`. The `*DeobfJar` and `*PlayerJar` +The Mavenizer fixture comes from OreSpawn's OS12111 checkout. Another layout +can use `-Pforge61MavenizerJar=`. The `*DeobfJar` and `*PlayerJar` properties select separate development and packaged inputs; both must satisfy the committed checksums. @@ -29,6 +35,8 @@ With the pinned local dependencies available: ```powershell $env:JAVA_HOME = "$env:USERPROFILE/.jdks/temurin-21.0.7+6" +$env:GRADLE_USER_HOME = 'D:/GradleCaches/basesteam-12111-port' +$env:JAVA_TOOL_OPTIONS = '-XX:ActiveProcessorCount=4' $jdk8 = "$env:USERPROFILE/.jdks/temurin-8.0.502+7/jdk8u502-b07" $jdks = "$env:JAVA_HOME,$env:USERPROFILE/.jdks/jdk-25.0.3+9,$jdk8" $toolchains = @("-Porg.gradle.java.installations.paths=$jdks", @@ -37,8 +45,8 @@ $toolchains = @("-Porg.gradle.java.installations.paths=$jdks", ./gradlew.bat @toolchains check build deobfJar verifyReleaseArtifacts writeReleaseChecksums ./gradlew.bat @toolchains verifyEclipseProductionClasspath ./gradlew.bat @toolchains -PsteamGameTestRunId=candidate verifyPlayerDependencies runGameTestServer -./tools/run-packaged-server-smoke.ps1 -ForgeInstall $env:FORGE_1211_SERVER_INSTALL -RunId candidate-with-metals -./tools/run-packaged-server-smoke.ps1 -ForgeInstall $env:FORGE_1211_SERVER_INSTALL -RunId candidate-without-metals -WithoutBaseMetals +./tools/run-packaged-server-smoke.ps1 -ForgeInstall $env:FORGE_12111_SERVER_INSTALL -RunId candidate-with-metals +./tools/run-packaged-server-smoke.ps1 -ForgeInstall $env:FORGE_12111_SERVER_INSTALL -RunId candidate-without-metals -WithoutBaseMetals ``` Independent source checks: `node tools/verify-native-port.js` and @@ -60,4 +68,6 @@ Use basePower's migration rig for whole-world checks. Before accepting the port, check machines, restarts, redstone, all drill directions, elevators, GUI captions, whole fluid gauges, track transparency and musket effects in-game. Automated, packaged and manual acceptance are separate gates. -See the [1.21.1 notes](docs/PORT1211-STATUS.md). +See the [1.21.11 notes](docs/PORT12111-STATUS.md). The +[1.21.1 notes](docs/PORT1211-STATUS.md) describe the accepted source baseline, +not qualification of this port. diff --git a/build.gradle b/build.gradle index a8f4d9b..6b0f22e 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ plugins { id 'net.minecraftforge.gradle' version '7.0.34' } -apply from: 'gradle/forge52-mavenizer.gradle' +apply from: 'gradle/forge61-mavenizer.gradle' group = project.mod_group version = project.mod_version @@ -133,6 +133,7 @@ minecraft { workingDir.set(steamGameTestRunDirectory) systemProperty 'forge.enableGameTest', 'true' systemProperty 'forge.enabledGameTestNamespaces', mod_id + args '--tests', "${mod_id}:*", '--report', 'steam-gametest-results.xml' mods { basesteam { source sourceSets.gameTestValidation } } } register('data') { @@ -151,7 +152,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 52 launches.' + description = 'Merges classes and processed resources for Forge 61 launches.' dependsOn tasks.named('classes') from sourceSets.main.output into forgeRunModClassesDirectory @@ -163,6 +164,7 @@ def prepareForgeGameTestModClasses = tasks.register('prepareForgeGameTestModClas into gameTestModClassesDirectory } tasks.withType(JavaExec).configureEach { + jvmArgs '-XX:ActiveProcessorCount=4' javaLauncher = javaToolchains.launcherFor { languageVersion = JavaLanguageVersion.of(21) vendor = JvmVendorSpec.ADOPTIUM @@ -203,6 +205,20 @@ tasks.withType(JavaExec).configureEach { if (!output.contains('All 4 required tests passed') || (output =~ /\[[^\]\r\n]*\/(?:ERROR|FATAL)\]/).find()) { throw new GradleException("The four Steam GameTests did not pass; see ${receipt}") } + File report = new File(steamGameTestRunDirectory.get().asFile, 'steam-gametest-results.xml') + if (!report.isFile()) throw new GradleException('Missing native GameTest XML report') + def results = new groovy.xml.XmlSlurper().parse(report) + def cases = results.depthFirst().findAll { it.name() == 'testcase' } + Set expectedTests = [ + 'drill_bit_packet_preserves_type_position_and_direction', + 'legacy_musket_wear_and_loaded_byte_survive_native_componentization', + 'older_musket_alias_and_top_level_wear_survive_power_migration', + 'native_resources_load_with_expected_ids' + ].collect { "${mod_id}:${it}".toString() } as Set + if (cases.size() != 4 || (cases.collect { it.@name.text() } as Set) != expectedTests + || cases.any { it.failure.size() || it.error.size() || it.skipped.size() }) { + throw new GradleException("Expected exactly four successful Steam GameTests without skips: ${report}") + } } } } @@ -230,12 +246,12 @@ repositories { // Only code and launch tasks require the exact jars; source/resource checks can run independently. def explicitJar = { String property -> providers.gradleProperty(property).map { file(it) }.getOrNull() } def powerRoot = '../../PowerAdvantage' -def baseMetalsRoot = '../../../../BaseMetals/.verification-basemetals1211/Advantage shared hook' +def baseMetalsRoot = '../../../../BaseMetals/.verification-basemetals12111/Advantage shared hook' def powerNativeJar = file("${powerRoot}/build/libs/basePower-${project.poweradvantage_version}.jar") def powerAdvantageDeobfJar = explicitJar('powerAdvantageDeobfJar') ?: powerNativeJar def baseMetalsDeobfJar = explicitJar('baseMetalsDeobfJar') ?: file( "${baseMetalsRoot}/build/libs/BaseMetals-${project.basemetals_version}.jar") -def oreSpawnRoot = '../../../../MinecraftMineralogy/OS1211/OreSpawn' +def oreSpawnRoot = '../../../../MinecraftMineralogy/OS12111/OreSpawn' def oreSpawnJar = { String variant -> explicitJar("oreSpawn${variant}Jar") ?: file( "${oreSpawnRoot}/build/libs/OreSpawn-${project.orespawn_version}.jar") @@ -292,6 +308,7 @@ def unitBootstrapSources = [ ] tasks.named('test', Test) { useJUnit() + jvmArgs '-XX:ActiveProcessorCount=4' inputs.files(forgeCapabilityBootstrapJar) inputs.files(unitBootstrapSources.values()) inputs.property('powerBootstrapSha256', providers.gradleProperty('poweradvantage_test_bootstrap_sha256')) @@ -322,6 +339,7 @@ tasks.named('test', Test) { } } logger.lifecycle("basePower test bootstrap: ${bootstrapJar.canonicalPath} SHA-256 ${actualHash}") + systemProperty 'basesteam.nativePackClasspath', sourceSets.test.runtimeClasspath.asPath jvmArgs "-javaagent:${bootstrapJar.canonicalPath}" jvmArgs '--add-opens', 'java.base/java.lang.invoke=ALL-UNNAMED' } @@ -360,7 +378,7 @@ tasks.named('jar', Jar) { } } -// Forge 52 runs official names. The player jar needs no SRG remapping. +// Forge 61 runs official names. The player jar needs no SRG remapping. def releaseJar = tasks.register('releaseJar', Jar) { dependsOn tasks.named('classes'), 'verifyReleaseDependencies' archiveClassifier = null @@ -437,8 +455,9 @@ def expectedReleaseFiles = providers.provider { def preparedReleaseDir = providers.gradleProperty('preparedReleaseDir') def verifyPackFormats = { pack -> - if (pack.pack_format != 34 || pack.supported_formats != [min_inclusive: 34, max_inclusive: 48]) { - throw new GradleException('Minecraft 1.21.1 requires resource format 34 and data format 48') + if (pack.min_format != [94, 1] || pack.max_format != [94, 1] + || pack.containsKey('pack_format') || pack.containsKey('supported_formats')) { + throw new GradleException('The mod root requires data format 94.1; Forge supplies hidden client pack metadata') } } @@ -454,15 +473,15 @@ tasks.register('verifyReleaseConfiguration') { '9c0f7faeeb306cb14e4279a3e084ca6b596894089a0638e68a07c945a32c9e14') { throw new GradleException('Use the checksum-pinned Gradle 9.6.1 wrapper') } - if (project.mod_version != '2.2.15.121011') { + if (project.mod_version != '2.2.16.121111') { throw new GradleException("Unexpected baseSteam release version: ${project.mod_version}") } - if (project.minecraft_version != '1.21.1' || project.minecraft_version_range != '[1.21.1]' - || project.forge_version != '52.1.0' || project.forge_version_range != '[52.1.0,53)' - || project.loader_version_range != '[52,53)' + if (project.minecraft_version != '1.21.11' || project.minecraft_version_range != '[1.21.11]' + || project.forge_version != '61.1.0' || project.forge_version_range != '[61.1.0,62)' + || project.loader_version_range != '[61,62)' || project.mapping_channel != 'official' - || project.mapping_version != '1.21.1' - || project.mcp_version != '20240808.132146') { + || project.mapping_version != '1.21.11' + || project.mcp_version != '20251223.124241') { throw new GradleException('Unexpected Minecraft, Forge, or mappings target') } if (project.loader_name != 'forge' || project.loader_code != '1' @@ -482,11 +501,11 @@ tasks.register('verifyReleaseConfiguration') { } if (project.cf_requirements != 'basepower,base-metals' || project.cf_optionals != '' - || project.cf_versions != '1.21.1') { + || project.cf_versions != '1.21.11') { throw new GradleException('Unexpected CurseForge dependency or game-version metadata') } - if (project.basemetals_version != '3.1.2.121011' - || project.orespawn_version != '4.1.0.121011') { + if (project.basemetals_version != '3.1.2.121111' + || project.orespawn_version != '4.1.0.121111') { throw new GradleException('Unexpected development API versions') } if (project.poweradvantage_source_repository != 'MinecraftModDevelopmentMods/basePower') { @@ -495,13 +514,13 @@ tasks.register('verifyReleaseConfiguration') { String mainSource = file( 'src/main/java/zone/moddev/mc/basesteam/SteamAdvantage.java').getText('UTF-8') if (!mainSource.contains('MODID = "basesteam"') - || !mainSource.contains('VERSION = "2.2.15.121011"')) { + || !mainSource.contains('VERSION = "2.2.16.121111"')) { throw new GradleException('baseSteam source identity changed') } String metadata = file('src/main/resources/META-INF/mods.toml').getText('UTF-8').replace('\r\n', '\n') - ['modId="basesteam"', 'displayName="baseSteam"', + ['showAsResourcePack=false', 'modId="basesteam"', 'displayName="baseSteam"', 'modId="basepower"\nmandatory=true\nversionRange="[${poweradvantage_version},3.0.0)"', - 'modId="basemetals"\nmandatory=false\nversionRange="[3.1.2.121011,4.0.0)"'].each { marker -> + 'modId="basemetals"\nmandatory=false\nversionRange="[3.1.2.121111,4.0.0)"'].each { marker -> if (!metadata.contains(marker)) { throw new GradleException("Source mods.toml is missing ${marker}") } @@ -587,7 +606,7 @@ tasks.register('verifyPlayerDependencies') { dependsOn tasks.named('verifyCompanionPlayerDependencies') doLast { committedPin('poweradvantage_source_ref', /[a-f0-9]{40}/) - committedPin('poweradvantage_version', /\d+\.\d+\.\d+\.121011/) + committedPin('poweradvantage_version', /\d+\.\d+\.\d+\.121111/) auditJar(explicitJar('powerAdvantagePlayerJar') ?: powerNativeJar, 'powerAdvantagePlayerJar', 'poweradvantage_player_sha256', 'basepower', project.poweradvantage_version, powerApi) } @@ -598,7 +617,7 @@ tasks.register('verifyNativeDevelopmentDependencies') { description = 'Resolves Forge and audits the exact development JARs.' dependsOn tasks.named('verifyCompanionDevelopmentDependencies') doLast { - committedPin('poweradvantage_version', /\d+\.\d+\.\d+\.121011/) + committedPin('poweradvantage_version', /\d+\.\d+\.\d+\.121111/) committedPin('poweradvantage_source_ref', /[a-f0-9]{40}/) auditJar(powerAdvantageDeobfJar, 'powerAdvantageDeobfJar', 'poweradvantage_deobf_sha256', 'basepower', project.poweradvantage_version, powerApi) @@ -637,10 +656,12 @@ tasks.register('verifyReleaseDependencies') { } def blocksAtlasPath = 'assets/minecraft/atlases/blocks.json' -def steamBlocksAtlas = [sources: [[type: 'single', resource: 'basesteam:entity/drill_bit']]] -def verifySteamBlocksAtlas = { Object atlas -> - if (atlas != steamBlocksAtlas) { - throw new GradleException("${blocksAtlasPath} must stitch only basesteam:entity/drill_bit as a single source") +def itemsAtlasPath = 'assets/minecraft/atlases/items.json' +def steamAtlasPaths = [blocksAtlasPath, itemsAtlasPath] +def steamDrillAtlas = [sources: [[type: 'single', resource: 'basesteam:entity/drill_bit']]] +def verifySteamDrillAtlas = { Object atlas -> + if (atlas != steamDrillAtlas) { + throw new GradleException('Steam block/item atlases must stitch only basesteam:entity/drill_bit as a single source') } } def verifyPackagedAssetNamespaces = { ZipFile zip -> @@ -648,11 +669,13 @@ def verifyPackagedAssetNamespaces = { ZipFile zip -> def foreignAssets = zip.entries().findAll { !it.directory && it.name.startsWith('assets/') && !it.name.startsWith(steamAssets) }.collect { it.name } - if (foreignAssets != [blocksAtlasPath]) { - throw new GradleException("${zip.name} must contain only ${blocksAtlasPath} outside ${steamAssets}; found ${foreignAssets}") + if (foreignAssets.sort() != steamAtlasPaths.sort(false)) { + throw new GradleException("${zip.name} must contain only ${steamAtlasPaths} outside ${steamAssets}; found ${foreignAssets}") } - zip.getInputStream(zip.getEntry(blocksAtlasPath)).withCloseable { input -> - verifySteamBlocksAtlas(new JsonSlurper().parse(input, 'UTF-8')) + steamAtlasPaths.each { path -> + zip.getInputStream(zip.getEntry(path)).withCloseable { input -> + verifySteamDrillAtlas(new JsonSlurper().parse(input, 'UTF-8')) + } } } @@ -719,6 +742,15 @@ tasks.register('verifyReleaseArtifacts') { throw new GradleException("Release jar is missing ${required}") } } + ['steam_pipe', 'steam_track', 'steam_boiler_coal', 'steam_boiler_electric', + 'steam_boiler_geothermal', 'steam_tank', 'steam_furnace', 'steam_crusher', + 'steam_drill', 'drillbit', 'platform', 'steam_elevator', 'steam_switch', + 'pump_pipe_steam', 'steam_still', 'steam_pump', 'steam_boiler_oil', + 'steam_governor', 'steam_drill_bit', 'blackpowder_cartridge', 'musket'].each { id -> + if (!names.contains("assets/basesteam/items/${id}.json".toString())) { + throw new GradleException("Release jar is missing native item definition ${id}") + } + } ['steam_pipe', 'steam_track', 'steam_boiler_coal', 'steam_boiler_electric', 'steam_boiler_geothermal', 'steam_tank', 'steam_furnace', 'steam_crusher', 'steam_drill', 'steam_elevator', 'steam_switch', 'steam_still', 'steam_pump', @@ -749,6 +781,7 @@ tasks.register('verifyReleaseArtifacts') { String metadata = zip.getInputStream(zip.getEntry('META-INF/mods.toml')) .getText(StandardCharsets.UTF_8.name()) if (!metadata.contains('modId="basesteam"') + || !metadata.contains('showAsResourcePack=false') || !metadata.contains("version=\"${project.mod_version}\"") || !metadata.contains("versionRange=\"${project.minecraft_version_range}\"") || !metadata.contains("versionRange=\"${project.forge_version_range}\"") @@ -1239,7 +1272,7 @@ tasks.register('verifyEclipseProductionClasspath') { || (name == 'runClient.launch' && !arguments.startsWith('--launcher-side client ')) || !vmArguments.contains('--add-opens java.base/java.lang.invoke=ALL-UNNAMED') || contents.contains('net.minecraftforge.userdev.LaunchTesting')) { - throw new GradleException("${name} must retain the native Forge 52 bootstrap and Java 21 module access") + throw new GradleException("${name} must retain the native Forge 61 bootstrap and Java 21 module access") } if (launch.booleanAttribute.find { it.@key.text() == 'org.eclipse.jdt.launching.ATTR_EXCLUDE_TEST_CODE' @@ -1297,7 +1330,7 @@ tasks.register('verifyEclipseProductionClasspath') { String target = name == 'runClient.launch' ? 'forge_userdev_client' : 'forge_userdev_server' if (!arguments.contains("--launchTarget ${target}") || !arguments.contains("forge-${project.minecraft_version}-${project.forge_version}-metadata.zip")) { - throw new GradleException("${name} does not target the exact Forge 52 development runtime") + throw new GradleException("${name} does not target the exact Forge 61 development runtime") } } } @@ -1311,9 +1344,9 @@ tasks.register('verifyModelAssets') { File assetRoot = new File(resourceRoot, "assets/${project.mod_id}") File modelRoot = new File(assetRoot, 'models') File blockstateRoot = new File(assetRoot, 'blockstates') - File steamAtlas = new File(resourceRoot, blocksAtlasPath) + List steamAtlases = steamAtlasPaths.collect { new File(resourceRoot, it) } inputs.dir(assetRoot) - inputs.files(fileTree(resourceRoot) { include blocksAtlasPath }) + inputs.files(steamAtlases) inputs.file(powerAdvantageDeobfJar) doLast { @@ -1323,22 +1356,27 @@ tasks.register('verifyModelAssets') { Set atlasPrefixes = ['block/', 'item/'] as Set Set singleSprites = [] as Set - if (steamAtlas.isFile()) { + steamAtlases.each { steamAtlas -> + if (!steamAtlas.isFile()) throw new GradleException("Missing native Steam atlas ${steamAtlas}") def atlas = new JsonSlurper().parse(steamAtlas) - verifySteamBlocksAtlas(atlas) + verifySteamDrillAtlas(atlas) singleSprites.addAll(atlas.sources.collect { it.resource }) } Set powerAdvantageAssets = new ZipFile(powerAdvantageDeobfJar).withCloseable { zip -> - def atlasEntry = zip.getEntry(blocksAtlasPath) - if (atlasEntry != null) { + Set sharedPrefixes = null + steamAtlasPaths.each { atlasPath -> + def atlasEntry = zip.getEntry(atlasPath) + if (atlasEntry == null) throw new GradleException("basePower is missing native atlas ${atlasPath}") zip.getInputStream(atlasEntry).withCloseable { input -> def atlas = new JsonSlurper().parse(input, 'UTF-8') // Power's directory sources preserve the existing texture IDs across namespaces. - atlas.sources.findAll { + Set prefixes = atlas.sources.findAll { it.type == 'directory' && it.source instanceof String && it.prefix == it.source + '/' - }.each { atlasPrefixes.add(it.prefix) } + }.collect { it.prefix } as Set + sharedPrefixes = sharedPrefixes == null ? prefixes : sharedPrefixes.intersect(prefixes) } } + atlasPrefixes.addAll(sharedPrefixes) zip.entries().collect { it.name } as Set } def verifyReference = { String reference, String assetType, String sourcePath -> @@ -1397,6 +1435,12 @@ tasks.register('verifyModelAssets') { } verifyBlockstateModels(blockstate, blockstateFile.path) } + File itemDefinitions = new File(assetRoot, 'items') + def definitions = fileTree(itemDefinitions) { include '*.json' }.files + if (definitions.size() != 21) throw new GradleException('Expected all 21 native item definitions') + definitions.sort().each { File definition -> + verifyBlockstateModels(new JsonSlurper().parse(definition), definition.path) + } } } @@ -1458,6 +1502,8 @@ tasks.register('verifyRecipeTagGuards') { } } else if (node instanceof Collection) { node.each { collectTags(it, tags) } + } else if (node instanceof String && node.startsWith('#forge:')) { + tags.add(node.substring(1)) } } fileTree(recipeRoot) { include '**/*.json' }.files.sort().each { File recipeFile -> diff --git a/docs/FORGE61-RESOURCE-METADATA.md b/docs/FORGE61-RESOURCE-METADATA.md new file mode 100644 index 0000000..b31ef55 --- /dev/null +++ b/docs/FORGE61-RESOURCE-METADATA.md @@ -0,0 +1,81 @@ +# Forge 61 Resource Metadata + +This applies to the exact Forge 1.21.11-61.1.0 target, not newer loaders. + +## Mod Metadata + +Keep pack.mcmeta at the mod root, declaring server data format 94.1: + +```json +{"pack":{"description":"baseSteam","min_format":[94,1],"max_format":[94,1]}} +``` + +Keep showAsResourcePack=false at the top level of META-INF/mods.toml. +It defaults to false, but the explicit value protects this contract. + +ResourcePackLoader uses PathPackResources for each mod. On the client it +combines hidden mods into DelegatingPackResources named mod_resources, +with native client metadata for 75.x. On the server it exposes the mod's +PathPackResources and reads the root's 94.1 metadata. A mod with +showAsResourcePack=true cannot use this hidden-client metadata strategy. + +Do not decode the server-format root as if it were the effective client +pack. The negative test still proves that a raw 94.1 resource pack does not +accept client format 75.0. Both effective native packs must be verified. + +A single raw [75,0] through [94,1] declaration fails server decoding because +the server's pre-minor threshold is 81 and requires legacy fields there. +The client's threshold is 64, so it rejects those legacy fields at 75. +Neither fallback metadata nor ignored diagnostic messages fixes that. + +Omitting pack.mcmeta is not a solution. The native mod PathResourcesSupplier +returns null metadata for both PackTypes. Pack.readPackMetadata then reports +missing metadata, and ResourcePackLoader enters its broken-resources warning +path. ForgeDevLocator's development-only fallback writes obsolete format 18; +it is not correct per-PackType metadata for a packaged 1.21.11 mod. + +## Native APIs + +MetadataSectionSerializer is replaced by MetadataSectionType. A native +PackResources implementation overrides: + +```java +public T getMetadataSection(MetadataSectionType section) throws IOException +``` + +PackMetadataSection takes a Component and InclusiveRange, not +an integer pack version. Use PackMetadataSection.forPackType(type) for the +correct codec, SharedConstants.getCurrentVersion().packVersion(type) for the +current format, and Pack.readMetaAndCreate(..., type, ...) when creating a +built-in pack. A server-only MaterialPolicyPack should use SERVER_DATA. +Native DelegatingPackResources is an example of directly supplied metadata. + +## Separate Loader Defect + +Forge 61.1.0 ResourcePackLoader.findPacks always gets its comparison version +from DetectedVersion.BUILT_IN.packVersion(CLIENT_RESOURCES), even for server +data. A correctly declared 94.1 server mod pack is therefore labelled +TOO_NEW by that discovery method. Re-reading the same native supplier with +the actual SERVER_DATA version returns COMPATIBLE, and every server resource +is accessible. The test receipt records the discrepancy explicitly. + +This is distinct from codec failure. It is not fixed or hidden here, and +server compatibility-label acceptance remains a parent/platform decision. +Do not change data to format 75, suppress warnings, or fake compatibility. + +## Proof + +NativePackResourceProof runs a separate JVM, builds a resource-only fixture +jar from Gradle's processed Steam resources, parses it with real SecureJar +and ModFile, and calls ResourcePackLoader through PackRepository. It then +opens the actual client/server packs and MultiPackResourceManager instances. +All 160 client and 91 server files are compared byte for byte with the input. +The effective metadata round-trips through the appropriate native codec, +rejects the opposite PackType's version and rejects out-of-range versions. +The Log4j appender observes WARN/ERROR messages without filtering existing +appenders, and the parent also checks the complete child log. Any diagnostic +fails the proof. The test child has its own working directory and leaves +its jar, logs and receipt under ignored build/native-pack-resources/. + +This proves resource/metadata behavior, not a full gameplay client/server +launch, basePower compatibility or the Steam release artifact. diff --git a/docs/PORT1211-STATUS.md b/docs/PORT1211-STATUS.md index 9d20435..f8a76bb 100644 --- a/docs/PORT1211-STATUS.md +++ b/docs/PORT1211-STATUS.md @@ -1,9 +1,8 @@ # Steam on Minecraft 1.21.1 -Local candidate 2.2.15.121011 targets Forge 52.1.0 and Java 21. -The build pins the coordinated local basePower commit `a16e8148871f` and -its exact native jar. Public CI still requires merged sources and verified -public files. Nothing has been published. +Version 2.2.15.121011 targets Forge 52.1.0 and Java 21. +The build pins released basePower 2.4.14.121011 at `8d9937b3c0c9`, with +public file IDs and checked jar hashes for all three dependencies. Steam keeps its mod ID, packages, registry paths, configuration and machine balance. Datapack registry folders use the native singular names, without @@ -19,6 +18,7 @@ plus four native GameTests, archive audits and the Eclipse classpath gate. Fresh packaged dedicated servers also start and stop cleanly with and without Base Metals. -Back up old worlds and use disposable copies with basePower's coordinated -migration rig. These Steam checks do not establish combined world-upgrade, -multiplayer or manual visual acceptance. +The coordinated suite also passed old-world migration, connected machine +jobs, saved-state checks, chunk unloading and two 30-minute endurance runs. +The client visual and recipe-unlock checks were accepted before publication. +Keep a backup and test upgrades on a copy; these checks cannot cover every modpack. diff --git a/docs/PORT12111-STATUS.md b/docs/PORT12111-STATUS.md new file mode 100644 index 0000000..885a66a --- /dev/null +++ b/docs/PORT12111-STATUS.md @@ -0,0 +1,88 @@ +# Steam on Minecraft 1.21.11 + +Local source port 2.2.16.121111 targets Forge 61.1.0, official mappings, +MCP 20251223.124241, Java 21, ForgeGradle 7.0.34 and Gradle 9.6.1. +The starting source is manually accepted 1.21.1 commit +9c5bf9b4610e06d3b5a6d5a0bde38d7516327d96. Its 228 unit tests and four +GameTests are a baseline, not qualification of this port. + +The approved local basePower 2.4.15.121111 integration candidate is pinned to +398439074a60bf9b8f8872f92485cffc4aa96be9 and native jar SHA-256 +0240E032075AF6B158887FBF98CE038504942329527036B82FD7CF9EEE602503 +with exact native unit-bootstrap and registry-support source hashes. The +default local dependency paths pass without path overrides. Public release +file IDs for local-only candidates remain empty and hosted publication gates +remain closed. +The screen-aware overload uses the native Matrix3x2fStack and retains its +independent pivot and sweep geometry checks. The deprecated global overload +retains its signature and delegates through the current native machine screen +only during its actual decoration callback. No shared Power API or runtime +jar change was needed. Submission and failure paths restore the caller matrix. +The local Base Metals 3.1.2.121111 shared-hook candidate is pinned to source +e6b869ffe7d6fd6a011a99f1844d2a7995394daf and jar SHA-256 +6D24D760E8E025960095DE04C51E6B79CFF93071618FC0B6BF64E05C346CC097. +OreSpawn 4.1.0.121111 is pinned to the published native jar. +Forge installer, server shim, server and universal hashes were checked against +the authentic local runtime seed and the proven native Base Metals reference. + +Persistent IDs, the legacy namespace, recipe identities, configuration +keys and saved machine fields are unchanged. The native port uses keyed +block/item properties, Identifier, Forge event bus groups, value-based +block-entity persistence, server recipe access and render-state submission. +Ingredients use native string/tag selectors. Native item definitions retain +the musket loaded byte and using-item model choices. + +The mod root declares data format [94,1]. showAsResourcePack=false keeps the +client assets in Forge's native hidden aggregate, which supplies format 75.x +metadata. Both former codec regressions now exercise actual Forge mod-file, +pack-discovery and resource-reader paths. No fallback metadata, ignored codec +warnings or synthetic replacement APIs are used. Omitting pack.mcmeta does +not synthesize suitable metadata in the native PathPackResources supplier. + +Forge 61.1.0 independently labels server mod packs using the client version +75.0, so its discovered server pack reports TOO_NEW even though native +SERVER_DATA metadata correctly accepts 94.1. The receipt records both values; +this platform compatibility-label issue remains open, not silently waived. +See FORGE61-RESOURCE-METADATA.md for parent integration guidance. + +The default local check/build/Javadoc/dependency/archive/checksum/Eclipse +metadata suite passes (43 tasks), with 242 unit tests and zero failures, +errors or skips. All three real Eclipse .classpath scopes compile using ECJ +3.46.0; those ECJ-compiled unit tests also pass with zero skips/assumptions. +Both source compilers include production, unit and GameTest Java sources. +All four native GameTests pass, selected by basesteam:* with exact named XML +results and no failures/skips. Forge 61's test-function registry and native +test_instance resources are confined to the isolated GameTest source scope. +The first launch's built-in-only one-test result correctly failed the gate. +Packaged runtime, combined migration/endurance, +cold/path-with-spaces reproducibility and manual acceptance remain open. +Nothing has been pushed, tagged or published. + +Merged 1.21.1 source b145f8d1a7c2a148f8f259faec7b203cf146b6b5 changes only +release pins and documentation from 9c5bf9b4610e06d3b5a6d5a0bde38d7516327d96. +Historical release notes were carried forward without importing 1.21.1 pins +or transferring its runtime acceptance to this port. + +The native item atlas now explicitly includes basesteam:entity/drill_bit in +addition to the block atlas. A native SpriteSources/resource-reader test +checks every Steam model texture against the exact Power and Steam packs, +including the legacy material IDs, and an omitted-Power negative control. + +Independent checks pass for the source/resource contracts, 24 recipe unlocks, +33 normalized recipe payloads and 113 unchanged protected resource files. +The two companion native jar audits pass. Eclipse metadata generation passes. +The earlier 27-test receipt with two pack-codec failures is retained. Current +native resource-reader tests verify 160 client and 91 server files byte for +byte from a resource-only fixture jar, with no WARN/ERROR diagnostics. +The refreshed independent batch compiles with javac and actual Eclipse ECJ and +passes 32 tests. Native Ingredient.CODEC controls cover strings, tags and +alternative arrays, rejecting legacy object ingredients, empty lists and +malformed selectors. ItemPredicate.CODEC controls prove the native discovery +predicates remain restrictive and expose the ignored old item field. +The generator's 24 checked-in unlocks remain unchanged and --check passes. +Earlier evidence is under ignored build/native-independent-26 and +build/steam-native-compile-22. Current full-source/archive evidence is under +build/native-pinned-28 and build/ecj-native61-first. Failed attempts remain +alongside later runs, including the old ValueOutput namespace false positive +and initial atlas-fixture setup failures. No gameplay or world-upgrade +qualification is implied by source/asset checks. diff --git a/gradle.properties b/gradle.properties index db5dda1..805f09e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,23 +1,24 @@ -org.gradle.jvmargs=-Xmx3G +org.gradle.jvmargs=-Xmx3G -XX:ActiveProcessorCount=4 +org.gradle.workers.max=2 org.gradle.daemon=false org.gradle.configuration-cache=false org.gradle.caching=true org.gradle.parallel=false net.minecraftforge.gradle.merge-source-sets=false -minecraft_version=1.21.1 -minecraft_version_range=[1.21.1] -forge_version=52.1.0 -forge_installer_sha256=F1B620F2879AD6A5BBE15DABA4D8F81EAB9F5E08004967604960B98996BDEBC4 -# Packaged smoke requires hashes from the coordinated, verified Forge 52 installation. -forge_server_shim_sha256=F7B706C79896728723DA0E4E8C5A6ECDB6A617AD4DF9D7EA1DDC6367187BC291 -forge_server_sha256=2DEA3FEFAD7DDF03E0A609CB2084A651732A35AE46DCF1D949D9359405A8621F -forge_universal_sha256=88EF6625122CFDB9D7B3176BD61B8C0692A191BAD0EDD77FFDDC6A765EE4979C -forge_version_range=[52.1.0,53) -loader_version_range=[52,53) +minecraft_version=1.21.11 +minecraft_version_range=[1.21.11] +forge_version=61.1.0 +# Verified against the parent's authentic Forge 61 runtime seed. +forge_installer_sha256=8BD1724EAEAF26F51DACFB7FDC46D79E9B640705B70D5F0019ED38907145EB48 +forge_server_shim_sha256=09008DB9881E35E62669865F3B7420741EDB1A52BC59C67A5FA03D24C6C8E6E3 +forge_server_sha256=8D997159003977A22DB8E4EA6DAAB6CBB0E76F752A196A1821C74734A1CF71D9 +forge_universal_sha256=101041931319D508D94EF9ED6643CA4374EA3950237443301F7AA5A3B2631CC4 +forge_version_range=[61.1.0,62) +loader_version_range=[61,62) mapping_channel=official -mapping_version=1.21.1 -mcp_version=20240808.132146 +mapping_version=1.21.11 +mcp_version=20251223.124241 loader_name=forge loader_code=1 @@ -30,36 +31,35 @@ curseforge_project_id=1781373 mod_id=basesteam mod_name=baseSteam mod_license=GPL-3.0-only -mod_version=2.2.15.121011 +mod_version=2.2.16.121111 mod_group=zone.moddev.mc.basesteam mod_authors=DrCyano mod_description=Steam-powered machines and infrastructure for basePower. cf_requirements=basepower,base-metals cf_optionals= -cf_versions=1.21.1 +cf_versions=1.21.11 -poweradvantage_version=2.4.14.121011 +poweradvantage_version=2.4.15.121111 poweradvantage_source_repository=MinecraftModDevelopmentMods/basePower -# Exact coordinated local candidate. Source and jar checks reject overrides. -# Public file IDs await independent public download verification; local approval is not publication. -poweradvantage_source_ref=a16e8148871fe7007bf1db5a982dbba5ddf1a1ef +# Exact approved LOCAL integration candidate; no public 1.21.11 release is implied. +poweradvantage_source_ref=398439074a60bf9b8f8872f92485cffc4aa96be9 poweradvantage_public_file_id= -poweradvantage_deobf_sha256=F91A613EB5FAA218AAD2DC3C881DEC7B897A5EE874215282A439E34B915C1AE6 -# Forge 52 uses the same native jar for development and packaged runtime. -poweradvantage_player_sha256=F91A613EB5FAA218AAD2DC3C881DEC7B897A5EE874215282A439E34B915C1AE6 -poweradvantage_test_bootstrap_sha256=6AF174EBBBF2501457CE4786A130AFDB6ED92CC47DD105465F9EF8685ECE90E3 -poweradvantage_test_bootstrap_source_sha256=CCEB755FDA6D38C492124DC122C993FA7EDA2A945CB603573D4CD9562B6AE280 -poweradvantage_test_registry_source_sha256=2E2C52DBAA14B4FA7EF1FF8C3A2BEFF9B1DF56CD6203C9CD692CAC1531D0981F +poweradvantage_deobf_sha256=0240E032075AF6B158887FBF98CE038504942329527036B82FD7CF9EEE602503 +# Forge 61 uses the same native jar for development and packaged runtime. +poweradvantage_player_sha256=0240E032075AF6B158887FBF98CE038504942329527036B82FD7CF9EEE602503 +poweradvantage_test_bootstrap_sha256=FDE28801E41787E05212F3DEF7FAF34C6859402A22B71866D15B468C300DA883 +poweradvantage_test_bootstrap_source_sha256=CDE40D09A40DB95F293BC56723E70B321C205850275696BEBFC2A575A8B7FE48 +poweradvantage_test_registry_source_sha256=01DDE50C9874C2DC501BE2F699CD8D16D6974FACFAAC1B803434CA8C64227ABF -basemetals_version=3.1.2.121011 -basemetals_source_commit=b42b92b0e6f60ac5baf07a6b74042b2fa8427904 +basemetals_version=3.1.2.121111 +basemetals_source_commit=e6b869ffe7d6fd6a011a99f1844d2a7995394daf basemetals_public_file_id= -basemetals_deobf_sha256=7C0B961A8D4991C9B69379518AAD700A71C1E4B2076CF5DEF292C58029408A07 -basemetals_player_sha256=7C0B961A8D4991C9B69379518AAD700A71C1E4B2076CF5DEF292C58029408A07 +basemetals_deobf_sha256=6D24D760E8E025960095DE04C51E6B79CFF93071618FC0B6BF64E05C346CC097 +basemetals_player_sha256=6D24D760E8E025960095DE04C51E6B79CFF93071618FC0B6BF64E05C346CC097 -orespawn_version=4.1.0.121011 -orespawn_source_commit=62c08169c71f4d4fd8f107289e63fd2a4c7c655c -orespawn_public_file_id= -orespawn_deobf_sha256=EB369B7D7CE6D6C453658347A420C2F0783B7F4D1AC7383C87176AC05192B5A0 -orespawn_player_sha256=EB369B7D7CE6D6C453658347A420C2F0783B7F4D1AC7383C87176AC05192B5A0 +orespawn_version=4.1.0.121111 +orespawn_source_commit=ecad75b24309a344f712d898d717c6a8823fc9f2 +orespawn_public_file_id=9268464 +orespawn_deobf_sha256=DE39929057288CEA110196C9E9D708E129B80BC360A2E53976337FFA6A84A67B +orespawn_player_sha256=DE39929057288CEA110196C9E9D708E129B80BC360A2E53976337FFA6A84A67B diff --git a/gradle/forge52-mavenizer.gradle b/gradle/forge52-mavenizer.gradle deleted file mode 100644 index 9031f95..0000000 --- a/gradle/forge52-mavenizer.gradle +++ /dev/null @@ -1,25 +0,0 @@ -import java.security.MessageDigest -import org.gradle.jvm.toolchain.JvmVendorSpec - -// Use OreSpawn's checksum-verified fixture for the native Forge 52 toolchain. -def tool = 'ci-fixtures/tools/minecraft-mavenizer-0.5.21-orespawn-compat.jar' -def defaultJar = "../../../../MinecraftMineralogy/OS1211/OreSpawn/${tool}" -def fixture = file(providers.gradleProperty('forge52MavenizerJar').getOrElse(defaultJar)) -def expected = '019032A4A26ECB473D403EF017054D5892CE59A9904E8831B452287CD756397E' -if (!fixture.isFile()) { - throw new GradleException("Forge 52 requires OreSpawn's Mavenizer fixture: ${fixture}. " - + "Use -Pforge52MavenizerJar= 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 52 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/gradle/forge61-mavenizer.gradle b/gradle/forge61-mavenizer.gradle new file mode 100644 index 0000000..f6bad96 --- /dev/null +++ b/gradle/forge61-mavenizer.gradle @@ -0,0 +1,31 @@ +import java.security.MessageDigest +import org.gradle.jvm.toolchain.JvmVendorSpec + +// Use OreSpawn's checksum-verified fixture for the native Forge 61 toolchain. +def tool = 'ci-fixtures/tools/minecraft-mavenizer-0.5.21-orespawn-compat.jar' +def defaultJar = "../../../../MinecraftMineralogy/OS12111/OreSpawn/${tool}" +def fixture = file(providers.gradleProperty('forge61MavenizerJar').getOrElse(defaultJar)) +def expected = 'B36CA046DB72E30F27BD38E02B4AE34274ADB51AE1D65BAF9E1D07763755682E' +if (!fixture.isFile()) { + throw new GradleException("Forge 61 requires OreSpawn's Mavenizer fixture: ${fixture}. " + + "Use -Pforge61MavenizerJar= 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 61 Mavenizer fixture SHA-256 mismatch: expected ${expected}, got ${actual}") +} +def rules = new File(fixture.parentFile, 'minecraft-source-compatibility.json') +def rulesHash = '88F2EE4C6B7C903EB1C7D693C0BEEFC0EFC601C1023FB197F7CBA187B1142DB7' +if (!rules.isFile() || MessageDigest.getInstance('SHA-256').digest(rules.bytes) + .encodeHex().toString().toUpperCase(Locale.ROOT) != rulesHash) { + throw new GradleException("Forge 61 Mavenizer source rules mismatch: ${rules}") +} + +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/gametest/java/zone/moddev/mc/basesteam/gametest/SteamGameTestRegistration.java b/src/gametest/java/zone/moddev/mc/basesteam/gametest/SteamGameTestRegistration.java new file mode 100644 index 0000000..3658a48 --- /dev/null +++ b/src/gametest/java/zone/moddev/mc/basesteam/gametest/SteamGameTestRegistration.java @@ -0,0 +1,28 @@ +package zone.moddev.mc.basesteam.gametest; + +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.Identifier; +import net.minecraftforge.eventbus.api.listener.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.registries.RegisterEvent; +import zone.moddev.mc.basesteam.SteamAdvantage; + +/** Native test functions are registered only in the isolated GameTest source scope. */ +@Mod.EventBusSubscriber(modid = SteamAdvantage.MODID, bus = Mod.EventBusSubscriber.Bus.MOD) +public final class SteamGameTestRegistration { + private SteamGameTestRegistration() {} + + @SubscribeEvent public static void register(RegisterEvent event) { + event.register(Registries.TEST_FUNCTION, registry -> { + registry.register(id("drill_bit_packet_preserves_type_position_and_direction"), + SteamNativeGameTests::drillBitPacketPreservesTypePositionAndDirection); + registry.register(id("legacy_musket_wear_and_loaded_byte_survive_native_componentization"), + SteamNativeGameTests::legacyMusketWearAndLoadedByteSurviveNativeComponentization); + registry.register(id("older_musket_alias_and_top_level_wear_survive_power_migration"), + SteamNativeGameTests::olderMusketAliasAndTopLevelWearSurvivePowerMigration); + registry.register(id("native_resources_load_with_expected_ids"), SteamNativeGameTests::nativeResourcesLoadWithExpectedIds); + }); + } + + private static Identifier id(String path) { return Identifier.fromNamespaceAndPath(SteamAdvantage.MODID, path); } +} diff --git a/src/gametest/java/zone/moddev/mc/basesteam/gametest/SteamNativeGameTests.java b/src/gametest/java/zone/moddev/mc/basesteam/gametest/SteamNativeGameTests.java index 479c474..9b80abf 100644 --- a/src/gametest/java/zone/moddev/mc/basesteam/gametest/SteamNativeGameTests.java +++ b/src/gametest/java/zone/moddev/mc/basesteam/gametest/SteamNativeGameTests.java @@ -3,12 +3,12 @@ import com.mojang.serialization.Dynamic; import io.netty.buffer.Unpooled; import net.minecraft.SharedConstants; -import net.minecraft.advancements.critereon.InventoryChangeTrigger; +import net.minecraft.advancements.criterion.InventoryChangeTrigger; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.component.DataComponents; import net.minecraft.core.registries.Registries; -import net.minecraft.gametest.framework.GameTest; +import net.minecraftforge.gametest.GameTest; import net.minecraft.gametest.framework.GameTestHelper; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; @@ -16,7 +16,7 @@ import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.tags.TagKey; import net.minecraft.util.datafix.DataFixers; import net.minecraft.util.datafix.fixes.References; @@ -27,7 +27,7 @@ import net.minecraft.world.level.storage.loot.parameters.LootContextParams; import net.minecraft.world.phys.Vec3; import net.minecraftforge.gametest.GameTestDontPrefix; -import net.minecraftforge.gametest.GameTestHolder; +import net.minecraftforge.gametest.GameTestNamespace; import zone.moddev.mc.basepower.migration.LegacyWorldDataHook; import zone.moddev.mc.basepower.util.ItemData; import zone.moddev.mc.basesteam.SteamAdvantage; @@ -38,16 +38,16 @@ import zone.moddev.mc.basesteam.init.Enchantments; import zone.moddev.mc.basesteam.items.MusketItem; -@GameTestHolder(value = SteamAdvantage.MODID, namespace = SteamAdvantage.MODID) +@GameTestNamespace(SteamAdvantage.MODID) @GameTestDontPrefix public final class SteamNativeGameTests { private SteamNativeGameTests() {} - @GameTest(batch = SteamAdvantage.MODID, template = "basesteam_empty") + @GameTest(structure = "basesteam:basesteam_empty") public static void drillBitPacketPreservesTypePositionAndDirection(GameTestHelper helper) { BlockPos local = new BlockPos(1, 1, 1); helper.setBlock(local, Blocks.drillbit); - DrillBitTileEntity source = (DrillBitTileEntity) helper.getBlockEntity(local); + DrillBitTileEntity source = helper.getBlockEntity(local, DrillBitTileEntity.class); var registries = helper.getLevel().registryAccess(); for (Direction direction : Direction.values()) { source.setDirection(direction); @@ -58,8 +58,9 @@ public static void drillBitPacketPreservesTypePositionAndDirection(GameTestHelpe helper.assertTrue(received.getType() == Entities.DRILLBIT, "Wrong drill-bit packet type"); helper.assertTrue(received.getPos().equals(helper.absolutePos(local)), "Wrong drill-bit position"); DrillBitTileEntity destination = new DrillBitTileEntity(received.getPos(), source.getBlockState()); - destination.onDataPacket(null, received, registries); - helper.assertTrue(destination.getUpdateTag(registries).getByte("dir") == direction.get3DDataValue(), + destination.onDataPacket(null, net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, registries, received.getTag()), registries); + helper.assertTrue(destination.getUpdateTag(registries).getByteOr("dir", (byte)0) == direction.get3DDataValue(), "Lost drill-bit direction " + direction); } finally { buffer.release(); @@ -68,7 +69,7 @@ public static void drillBitPacketPreservesTypePositionAndDirection(GameTestHelpe helper.succeed(); } - @GameTest(batch = SteamAdvantage.MODID, template = "basesteam_empty") + @GameTest(structure = "basesteam:basesteam_empty") public static void legacyMusketWearAndLoadedByteSurviveNativeComponentization(GameTestHelper helper) { CompoundTag legacy = new CompoundTag(); legacy.putString("id", "basesteam:musket"); @@ -81,23 +82,24 @@ public static void legacyMusketWearAndLoadedByteSurviveNativeComponentization(Ga legacy.put("tag", tag); var registries = helper.getLevel().registryAccess(); var fixed = DataFixers.getDataFixer().update(References.ITEM_STACK, - new Dynamic<>(NbtOps.INSTANCE, legacy), 3465, SharedConstants.getCurrentVersion().getDataVersion().getVersion()); - ItemStack stack = ItemStack.parse(registries, fixed.getValue()).orElseThrow(); + new Dynamic<>(NbtOps.INSTANCE, legacy), 3465, SharedConstants.getCurrentVersion().dataVersion().version()); + ItemStack stack = ItemStack.CODEC.parse(registries.createSerializationContext(NbtOps.INSTANCE), fixed.getValue()).getOrThrow(); helper.assertTrue(stack.is(Items.blackpowder_musket), "Converted the wrong item"); helper.assertTrue(stack.getDamageValue() == 249 && stack.getMaxDamage() == 250, "Lost legacy wear"); helper.assertTrue(MusketItem.isLoaded(stack), "Lost the legacy loaded byte"); assertMusketEnchantments(helper, stack); MusketItem.unload(stack); - ItemStack reloaded = ItemStack.parse(registries, stack.save(registries)).orElseThrow(); + ItemStack reloaded = ItemStack.CODEC.parse(registries.createSerializationContext(net.minecraft.nbt.NbtOps.INSTANCE), + ItemStack.CODEC.encodeStart(registries.createSerializationContext(net.minecraft.nbt.NbtOps.INSTANCE), stack).getOrThrow()).getOrThrow(); helper.assertTrue(MusketItem.isNotLoaded(reloaded) && reloaded.getDamageValue() == 249, "Changing loaded state must not reset wear"); - helper.assertTrue("legacy fixture".equals(reloaded.get(DataComponents.CUSTOM_DATA).copyTag().getString("owner")), + helper.assertTrue("legacy fixture".equals(reloaded.get(DataComponents.CUSTOM_DATA).copyTag().getStringOr("owner", "")), "Changing loaded state must not discard unrelated legacy data"); assertMusketEnchantments(helper, reloaded); helper.succeed(); } - @GameTest(batch = SteamAdvantage.MODID, template = "basesteam_empty") + @GameTest(structure = "basesteam:basesteam_empty") public static void olderMusketAliasAndTopLevelWearSurvivePowerMigration(GameTestHelper helper) { CompoundTag legacy = new CompoundTag(); legacy.putString("id", "steamadvantage:musket"); @@ -116,22 +118,22 @@ public static void olderMusketAliasAndTopLevelWearSurvivePowerMigration(GameTest helper.succeed(); } - @GameTest(batch = SteamAdvantage.MODID, template = "basesteam_empty") + @GameTest(structure = "basesteam:basesteam_empty") public static void nativeResourcesLoadWithExpectedIds(GameTestHelper helper) { var level = helper.getLevel(); var registries = level.registryAccess(); - var nativeEnchantments = registries.registryOrThrow(Registries.ENCHANTMENT); + var nativeEnchantments = registries.lookupOrThrow(Registries.ENCHANTMENT); for (var key : java.util.List.of(Enchantments.high_explosive, Enchantments.powderless, Enchantments.rapid_reload, Enchantments.recoil)) { - var holder = nativeEnchantments.getHolderOrThrow(key); + var holder = nativeEnchantments.getOrThrow(key); helper.assertTrue(holder.is(net.minecraft.tags.EnchantmentTags.IN_ENCHANTING_TABLE), - "Missing table discovery for " + key.location()); + "Missing table discovery for " + key.identifier()); helper.assertTrue(holder.is(net.minecraft.tags.EnchantmentTags.TRADEABLE), - "Missing enchanted-book trade discovery for " + key.location()); + "Missing enchanted-book trade discovery for " + key.identifier()); helper.assertTrue(holder.is(net.minecraft.tags.EnchantmentTags.ON_RANDOM_LOOT), - "Missing random loot discovery for " + key.location()); + "Missing random loot discovery for " + key.identifier()); helper.assertTrue(holder.value().isPrimaryItem(new ItemStack(Items.blackpowder_musket)), - "Native musket support tag was not bound for " + key.location()); + "Native musket support tag was not bound for " + key.identifier()); } var fragile = TagKey.create(Registries.BLOCK, id("musket_fragile")); helper.assertTrue(net.minecraft.world.level.block.Blocks.SHORT_GRASS.defaultBlockState().is(fragile), @@ -165,12 +167,13 @@ public static void nativeResourcesLoadWithExpectedIds(GameTestHelper helper) { if (!chestTable.getRandomItems(params, seed).isEmpty()) foundSteamItem = true; } helper.assertTrue(foundSteamItem, "Steam chest rolls returned no items"); - helper.assertTrue(loot.getLootTable(Blocks.steam_pipe.getLootTable()) != LootTable.EMPTY, + helper.assertTrue(loot.getLootTable(Blocks.steam_pipe.getLootTable().orElseThrow()) != LootTable.EMPTY, "Missing Steam pipe block loot"); - var pipe = level.getRecipeManager().byKey(id("steam_pipe")); - var brass = net.minecraft.tags.ItemTags.create(ResourceLocation.fromNamespaceAndPath("forge", "ingots/brass")); + var pipe = level.recipeAccess().byKey(ResourceKey.create(Registries.RECIPE, id("steam_pipe"))); + var brass = net.minecraft.tags.ItemTags.create(Identifier.fromNamespaceAndPath("forge", "ingots/brass")); if (registries.lookupOrThrow(Registries.ITEM).get(brass).filter(tag -> tag.size() > 0).isPresent()) { - var result = pipe.orElseThrow().value().getResultItem(registries); + var recipe = (net.minecraft.world.item.crafting.ShapedRecipe)pipe.orElseThrow().value(); + var result = recipe.assemble(net.minecraft.world.item.crafting.CraftingInput.EMPTY, registries); helper.assertTrue(result.is(Blocks.steam_pipe.asItem()) && result.getCount() == 6, "Wrong Steam pipe recipe output"); } else { @@ -179,8 +182,8 @@ public static void nativeResourcesLoadWithExpectedIds(GameTestHelper helper) { helper.succeed(); } - private static ResourceLocation id(String path) { - return ResourceLocation.fromNamespaceAndPath(SteamAdvantage.MODID, path); + private static Identifier id(String path) { + return Identifier.fromNamespaceAndPath(SteamAdvantage.MODID, path); } private static ListTag legacyEnchantments(String namespace) { @@ -200,7 +203,7 @@ private static void assertMusketEnchantments(GameTestHelper helper, ItemStack st Enchantments.rapid_reload, Enchantments.recoil)) { int expected = key.equals(Enchantments.high_explosive) || key.equals(Enchantments.recoil) ? 5 : 1; helper.assertTrue(Enchantments.level(key, stack, registries) == expected, - "Lost legacy enchantment holder or level: " + key.location()); + "Lost legacy enchantment holder or level: " + key.identifier()); } } } diff --git a/src/gametest/resources/data/basesteam/test_instance/drill_bit_packet_preserves_type_position_and_direction.json b/src/gametest/resources/data/basesteam/test_instance/drill_bit_packet_preserves_type_position_and_direction.json new file mode 100644 index 0000000..a26ddb9 --- /dev/null +++ b/src/gametest/resources/data/basesteam/test_instance/drill_bit_packet_preserves_type_position_and_direction.json @@ -0,0 +1,7 @@ +{ + "type": "minecraft:function", + "function": "basesteam:drill_bit_packet_preserves_type_position_and_direction", + "environment": "minecraft:default", + "structure": "basesteam:basesteam_empty", + "max_ticks": 100 +} diff --git a/src/gametest/resources/data/basesteam/test_instance/legacy_musket_wear_and_loaded_byte_survive_native_componentization.json b/src/gametest/resources/data/basesteam/test_instance/legacy_musket_wear_and_loaded_byte_survive_native_componentization.json new file mode 100644 index 0000000..abfb9ff --- /dev/null +++ b/src/gametest/resources/data/basesteam/test_instance/legacy_musket_wear_and_loaded_byte_survive_native_componentization.json @@ -0,0 +1,7 @@ +{ + "type": "minecraft:function", + "function": "basesteam:legacy_musket_wear_and_loaded_byte_survive_native_componentization", + "environment": "minecraft:default", + "structure": "basesteam:basesteam_empty", + "max_ticks": 100 +} diff --git a/src/gametest/resources/data/basesteam/test_instance/native_resources_load_with_expected_ids.json b/src/gametest/resources/data/basesteam/test_instance/native_resources_load_with_expected_ids.json new file mode 100644 index 0000000..7947b54 --- /dev/null +++ b/src/gametest/resources/data/basesteam/test_instance/native_resources_load_with_expected_ids.json @@ -0,0 +1,7 @@ +{ + "type": "minecraft:function", + "function": "basesteam:native_resources_load_with_expected_ids", + "environment": "minecraft:default", + "structure": "basesteam:basesteam_empty", + "max_ticks": 100 +} diff --git a/src/gametest/resources/data/basesteam/test_instance/older_musket_alias_and_top_level_wear_survive_power_migration.json b/src/gametest/resources/data/basesteam/test_instance/older_musket_alias_and_top_level_wear_survive_power_migration.json new file mode 100644 index 0000000..ba25e0f --- /dev/null +++ b/src/gametest/resources/data/basesteam/test_instance/older_musket_alias_and_top_level_wear_survive_power_migration.json @@ -0,0 +1,7 @@ +{ + "type": "minecraft:function", + "function": "basesteam:older_musket_alias_and_top_level_wear_survive_power_migration", + "environment": "minecraft:default", + "structure": "basesteam:basesteam_empty", + "max_ticks": 100 +} diff --git a/src/main/java/zone/moddev/mc/basesteam/SteamAdvantage.java b/src/main/java/zone/moddev/mc/basesteam/SteamAdvantage.java index 0afc165..7ad2d4e 100644 --- a/src/main/java/zone/moddev/mc/basesteam/SteamAdvantage.java +++ b/src/main/java/zone/moddev/mc/basesteam/SteamAdvantage.java @@ -9,7 +9,6 @@ import org.apache.logging.log4j.Logger; import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.fml.DistExecutor; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.config.ModConfig; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; @@ -27,12 +26,12 @@ import zone.moddev.mc.basesteam.migration.SteamConfigMigration; import zone.moddev.mc.basesteam.recipe.RecipeConditions; -/** Forge 1.21.1 entry point for baseSteam. */ +/** Forge 1.21.11 entry point for baseSteam. */ @Mod(SteamAdvantage.MODID) public final class SteamAdvantage { public static final String MODID = "basesteam"; public static final String NAME = "baseSteam"; - public static final String VERSION = "2.2.15.121011"; + public static final String VERSION = "2.2.16.121111"; public static final Logger LOGGER = LogManager.getLogger(MODID); public static float MUSKET_DAMAGE = 20.0F; @@ -45,13 +44,15 @@ public SteamAdvantage(FMLJavaModLoadingContext context) { migrateLegacyConfiguration(); context.registerConfig(ModConfig.Type.COMMON, SteamAdvantageConfig.SPEC); - var modBus = context.getModEventBus(); - modBus.addListener(SteamAdvantageConfig::onConfigLoading); - modBus.addListener(this::commonSetup); + var modBus = context.getModBusGroup(); + net.minecraftforge.fml.event.config.ModConfigEvent.Loading.getBus(modBus) + .addListener(SteamAdvantageConfig::onConfigLoading); + FMLCommonSetupEvent.getBus(modBus).addListener(this::commonSetup); RecipeConditions.register(modBus); - DistExecutor.runWhenOn(Dist.CLIENT, - () -> () -> zone.moddev.mc.basesteam.graphics.ClientRegistration.register(modBus)); + if (net.minecraftforge.fml.loading.FMLEnvironment.dist == Dist.CLIENT) { + zone.moddev.mc.basesteam.graphics.ClientRegistration.register(modBus); + } } private static void migrateLegacyConfiguration() { diff --git a/src/main/java/zone/moddev/mc/basesteam/blocks/DrillBitBlock.java b/src/main/java/zone/moddev/mc/basesteam/blocks/DrillBitBlock.java index 1e57274..1857ac8 100644 --- a/src/main/java/zone/moddev/mc/basesteam/blocks/DrillBitBlock.java +++ b/src/main/java/zone/moddev/mc/basesteam/blocks/DrillBitBlock.java @@ -20,7 +20,7 @@ public class DrillBitBlock extends Block implements EntityBlock { private static final VoxelShape SHAPE = Shapes.box(0.25D, 0.25D, 0.25D, 0.75D, 0.75D, 0.75D); public DrillBitBlock() { - super(SteamBlockProperties.metalHelper(2000.0F)); + super(SteamBlockProperties.metalHelper("drillbit", 2000.0F)); } @Override public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { return new DrillBitTileEntity(pos, state); } @@ -35,29 +35,21 @@ public net.minecraft.world.level.block.entity.BlockEntit @Override public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) { return SHAPE; } @Override - public void entityInside(BlockState state, Level world, BlockPos pos, Entity victim) { - if (!world.isClientSide) victim.hurt(Power.machineDamage(world), 2.0F); - } - - @Override - public void onRemove(BlockState state, Level world, BlockPos pos, BlockState newState, boolean moving) { - if (state.getBlock() != newState.getBlock()) { - BlockEntity tile = world.getBlockEntity(pos); - if (!world.isClientSide && tile instanceof DrillBitTileEntity) { - ((DrillBitTileEntity) tile).destroyLine(false); - } + public void entityInside(BlockState state, Level world, BlockPos pos, Entity victim, + net.minecraft.world.entity.InsideBlockEffectApplier effects, boolean moving) { + if (world instanceof net.minecraft.server.level.ServerLevel server) { + victim.hurtServer(server, Power.machineDamage(world), 2.0F); } - super.onRemove(state, world, pos, newState, moving); } @Override - public void wasExploded(Level world, BlockPos pos, Explosion explosion) { + public void wasExploded(net.minecraft.server.level.ServerLevel world, BlockPos pos, Explosion explosion) { destroyNeighbors(world, pos); super.wasExploded(world, pos, explosion); } private void destroyNeighbors(Level world, BlockPos pos) { - if (world.isClientSide) return; + if (world.isClientSide()) return; for (Direction side : Direction.values()) { BlockEntity tile = world.getBlockEntity(pos.relative(side)); if (tile instanceof DrillBitTileEntity) ((DrillBitTileEntity) tile).destroyLine(); diff --git a/src/main/java/zone/moddev/mc/basesteam/blocks/DrillBitTileEntity.java b/src/main/java/zone/moddev/mc/basesteam/blocks/DrillBitTileEntity.java index 06171b8..a8e82d8 100644 --- a/src/main/java/zone/moddev/mc/basesteam/blocks/DrillBitTileEntity.java +++ b/src/main/java/zone/moddev/mc/basesteam/blocks/DrillBitTileEntity.java @@ -27,7 +27,7 @@ public DrillBitTileEntity(BlockPos pos, BlockState state) { } public void tick(){ - if(hasLevel() && getLevel().isClientSide){ + if(hasLevel() && getLevel().isClientSide()){ rotation = ROTATION_PER_TICK * (getLevel().getGameTime() % 20); } } @@ -40,6 +40,12 @@ public void setDirection(Direction dir){ public Direction.Axis getDirection(){ return direction.getAxis(); } + + @Override + public void preRemoveSideEffects(BlockPos pos, BlockState state) { + destroyLine(false); + super.preRemoveSideEffects(pos, state); + } public static void createDrillBitBlock(Level w, BlockPos coord, Direction dir){ w.setBlockAndUpdate(coord, zone.moddev.mc.basesteam.init.Blocks.drillbit.defaultBlockState()); @@ -55,7 +61,7 @@ public void destroyLine(){ } public void destroyLine(boolean removeOrigin){ - if(!hasLevel() || getLevel().isClientSide) return; + if(!hasLevel() || getLevel().isClientSide()) return; List line = new ArrayList<>(); line.add(getBlockPos()); collectLine(line, direction); @@ -78,16 +84,16 @@ private void collectLine(List line, Direction f){ } @Override - protected void saveAdditional(CompoundTag root, HolderLookup.Provider registries){ - super.saveAdditional(root, registries); - writeDirectionToNBT(root); + protected void saveAdditional(net.minecraft.world.level.storage.ValueOutput root){ + super.saveAdditional(root); + root.putByte("dir", (byte)direction.get3DDataValue()); } @Override - protected void loadAdditional(CompoundTag root, HolderLookup.Provider registries){ - super.loadAdditional(root, registries); - readDirectionFromNBT(root); + protected void loadAdditional(net.minecraft.world.level.storage.ValueInput root){ + super.loadAdditional(root); + this.direction = decodeDirection(root.getByteOr("dir", root.getByteOr("d", (byte)direction.get3DDataValue()))); } @Override @@ -109,20 +115,12 @@ private void writeDirectionToNBT(CompoundTag root) { root.putByte("dir", (byte)direction.get3DDataValue()); } - private void readDirectionFromNBT(CompoundTag root) { - if(root.contains("dir")){ - this.direction = decodeDirection(root.getByte("dir")); - } else if(root.contains("d")){ - this.direction = decodeDirection(root.getByte("d")); - } - } - private static Direction decodeDirection(int index) { return index >= 0 && index < Direction.values().length ? Direction.from3DDataValue(index) : Direction.DOWN; } private void syncDirection() { - if(hasLevel() && !getLevel().isClientSide) { + if(hasLevel() && !getLevel().isClientSide()) { setChanged(); BlockState state = getLevel().getBlockState(getBlockPos()); getLevel().sendBlockUpdated(getBlockPos(), state, state, 3); diff --git a/src/main/java/zone/moddev/mc/basesteam/blocks/PlatformBlock.java b/src/main/java/zone/moddev/mc/basesteam/blocks/PlatformBlock.java index 4100996..09a1847 100644 --- a/src/main/java/zone/moddev/mc/basesteam/blocks/PlatformBlock.java +++ b/src/main/java/zone/moddev/mc/basesteam/blocks/PlatformBlock.java @@ -24,7 +24,7 @@ public class PlatformBlock extends Block { }; public PlatformBlock() { - super(SteamBlockProperties.metalHelper(2000.0F) + super(SteamBlockProperties.metalHelper("platform", 2000.0F) .isRedstoneConductor((state, world, pos) -> false) .isSuffocating((state, world, pos) -> false) .isViewBlocking((state, world, pos) -> false)); @@ -36,7 +36,8 @@ public PlatformBlock() { @Override public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) { return SHAPES[state.getValue(HEIGHT)]; } @Override - public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean moving) { + public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, + net.minecraft.world.level.redstone.Orientation orientation, boolean moving) { if (!canBlockStay(world, pos)) world.destroyBlock(pos, true); } diff --git a/src/main/java/zone/moddev/mc/basesteam/blocks/PumpPipeBlock.java b/src/main/java/zone/moddev/mc/basesteam/blocks/PumpPipeBlock.java index dbdb175..cdd2597 100644 --- a/src/main/java/zone/moddev/mc/basesteam/blocks/PumpPipeBlock.java +++ b/src/main/java/zone/moddev/mc/basesteam/blocks/PumpPipeBlock.java @@ -15,26 +15,27 @@ public class PumpPipeBlock extends Block { private static final VoxelShape SHAPE = Shapes.box(0.0D, 0.25D, 0.0D, 0.75D, 1.0D, 1.0D); public PumpPipeBlock() { - super(SteamBlockProperties.metalHelper(10.0F)); + super(SteamBlockProperties.metalHelper("pump_pipe_steam", 10.0F)); } @Override public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) { return SHAPE; } @Override public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) { return SHAPE; } @Override - public void onRemove(BlockState state, Level world, BlockPos pos, BlockState newState, boolean moving) { - if (state.getBlock() != newState.getBlock()) destroyShaft(world, pos); - super.onRemove(state, world, pos, newState, moving); + public void affectNeighborsAfterRemoval(BlockState state, net.minecraft.server.level.ServerLevel world, + BlockPos pos, boolean moving) { + destroyShaft(world, pos); + super.affectNeighborsAfterRemoval(state, world, pos, moving); } @Override - public void wasExploded(Level world, BlockPos pos, Explosion explosion) { + public void wasExploded(net.minecraft.server.level.ServerLevel world, BlockPos pos, Explosion explosion) { destroyShaft(world, pos); super.wasExploded(world, pos, explosion); } private void destroyShaft(Level world, BlockPos pos) { - if (world.isClientSide) return; + if (world.isClientSide()) return; BlockPos cursor = pos.below(); while (cursor.getY() > 0 && world.getBlockState(cursor).getBlock() == this) { world.destroyBlock(cursor, false); diff --git a/src/main/java/zone/moddev/mc/basesteam/blocks/SteamBlockProperties.java b/src/main/java/zone/moddev/mc/basesteam/blocks/SteamBlockProperties.java index 8f17043..51d381f 100644 --- a/src/main/java/zone/moddev/mc/basesteam/blocks/SteamBlockProperties.java +++ b/src/main/java/zone/moddev/mc/basesteam/blocks/SteamBlockProperties.java @@ -5,16 +5,21 @@ import net.minecraft.world.level.material.PushReaction; /** Native block properties matching the old piston-like construction material. */ -final class SteamBlockProperties { +public final class SteamBlockProperties { private SteamBlockProperties() {} - static Block.Properties pistonLike() { - return Block.Properties.of().mapColor(MapColor.STONE).pushReaction(PushReaction.BLOCK) + public static net.minecraft.resources.ResourceKey key(String path) { + return net.minecraft.resources.ResourceKey.create(net.minecraft.core.registries.Registries.BLOCK, + net.minecraft.resources.Identifier.fromNamespaceAndPath("basesteam", path)); + } + + public static Block.Properties pistonLike(String path) { + return Block.Properties.of().setId(key(path)).mapColor(MapColor.STONE).pushReaction(PushReaction.BLOCK) .forceSolidOn().strength(0.75F); } - static Block.Properties metalHelper(float resistance) { - return Block.Properties.of().mapColor(MapColor.METAL).forceSolidOn().strength(5.0F, resistance) + static Block.Properties metalHelper(String path, float resistance) { + return Block.Properties.of().setId(key(path)).mapColor(MapColor.METAL).forceSolidOn().strength(5.0F, resistance) .noOcclusion().noLootTable(); } } diff --git a/src/main/java/zone/moddev/mc/basesteam/blocks/SteamPipeBlock.java b/src/main/java/zone/moddev/mc/basesteam/blocks/SteamPipeBlock.java index a8558b0..49949d7 100644 --- a/src/main/java/zone/moddev/mc/basesteam/blocks/SteamPipeBlock.java +++ b/src/main/java/zone/moddev/mc/basesteam/blocks/SteamPipeBlock.java @@ -5,7 +5,7 @@ public class SteamPipeBlock extends cyano.poweradvantage.api.simple.BlockSimplePowerConduit{ public SteamPipeBlock() { - super(SteamBlockProperties.pistonLike(), 2f/16f, Power.steam_power); + super(SteamBlockProperties.pistonLike("steam_pipe"), 2f/16f, Power.steam_power); } } diff --git a/src/main/java/zone/moddev/mc/basesteam/blocks/SteamTrackBlock.java b/src/main/java/zone/moddev/mc/basesteam/blocks/SteamTrackBlock.java index 932a78a..eb9ab3f 100644 --- a/src/main/java/zone/moddev/mc/basesteam/blocks/SteamTrackBlock.java +++ b/src/main/java/zone/moddev/mc/basesteam/blocks/SteamTrackBlock.java @@ -11,7 +11,7 @@ public class SteamTrackBlock extends BlockSimplePowerConduit { public SteamTrackBlock() { - super(SteamBlockProperties.pistonLike().noOcclusion() + super(SteamBlockProperties.pistonLike("steam_track").noOcclusion() .isRedstoneConductor((state, world, pos) -> false) .isSuffocating((state, world, pos) -> false) .isViewBlocking((state, world, pos) -> false), diff --git a/src/main/java/zone/moddev/mc/basesteam/compat/BaseMetalsCompat.java b/src/main/java/zone/moddev/mc/basesteam/compat/BaseMetalsCompat.java index 6d79986..67de180 100644 --- a/src/main/java/zone/moddev/mc/basesteam/compat/BaseMetalsCompat.java +++ b/src/main/java/zone/moddev/mc/basesteam/compat/BaseMetalsCompat.java @@ -37,8 +37,10 @@ public static ItemStack getCrusherRecipeOutput(Level world, ItemStack input) { private static final class LoadedCrushingRecipes { private static ItemStack find(Level world, ItemStack input) { SingleRecipeInput inventory = new SingleRecipeInput(input.copy()); - for (var candidate : world.getRecipeManager().getAllRecipesFor(CrushingRecipe.TYPE.get())) { - CrushingRecipe crushing = candidate.value(); + if (!(world instanceof net.minecraft.server.level.ServerLevel server)) return null; + for (var candidate : server.recipeAccess().getRecipes().stream() + .filter(holder -> CrushingRecipe.TYPE.get().equals(holder.value().getType())).toList()) { + CrushingRecipe crushing = (CrushingRecipe)candidate.value(); if (crushing.matches(inventory, world)) { return crushing.getResultItem(world.registryAccess()).copy(); } diff --git a/src/main/java/zone/moddev/mc/basesteam/graphics/ClientRegistration.java b/src/main/java/zone/moddev/mc/basesteam/graphics/ClientRegistration.java index 558b145..d67e505 100644 --- a/src/main/java/zone/moddev/mc/basesteam/graphics/ClientRegistration.java +++ b/src/main/java/zone/moddev/mc/basesteam/graphics/ClientRegistration.java @@ -3,12 +3,7 @@ import net.minecraftforge.client.event.EntityRenderersEvent; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import net.minecraftforge.eventbus.api.IEventBus; -import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; -import net.minecraft.client.renderer.item.ItemProperties; -import net.minecraft.resources.ResourceLocation; -import zone.moddev.mc.basesteam.init.Items; -import zone.moddev.mc.basesteam.items.MusketItem; +import net.minecraftforge.eventbus.api.bus.BusGroup; import zone.moddev.mc.basesteam.init.Entities; /** Client-only renderer registration. */ @@ -16,22 +11,12 @@ public final class ClientRegistration { private ClientRegistration() {} - public static void register(IEventBus modBus) { - modBus.addListener(ClientRegistration::onClientSetup); - modBus.addListener(ClientRegistration::registerRenderers); + public static void register(BusGroup modBus) { + EntityRenderersEvent.RegisterRenderers.BUS.addListener(ClientRegistration::registerRenderers); } private static void registerRenderers(EntityRenderersEvent.RegisterRenderers event) { event.registerBlockEntityRenderer(Entities.DRILLBIT, DrillBitRenderer::new); } - private static void onClientSetup(FMLClientSetupEvent event) { - event.enqueueWork(() -> { - ItemProperties.register(Items.blackpowder_musket, ResourceLocation.withDefaultNamespace("loaded"), - (item, world, entity, seed) -> MusketItem.isLoaded(item) ? 1.0F : 0.0F); - ItemProperties.register(Items.blackpowder_musket, ResourceLocation.withDefaultNamespace("loading"), - (item, world, entity, seed) -> entity != null && entity.isUsingItem() - && entity.getUseItem() == item ? 1.0F : 0.0F); - }); - } } diff --git a/src/main/java/zone/moddev/mc/basesteam/graphics/DrillBitRenderer.java b/src/main/java/zone/moddev/mc/basesteam/graphics/DrillBitRenderer.java index 94dfb6e..237a72b 100644 --- a/src/main/java/zone/moddev/mc/basesteam/graphics/DrillBitRenderer.java +++ b/src/main/java/zone/moddev/mc/basesteam/graphics/DrillBitRenderer.java @@ -2,44 +2,80 @@ import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.SubmitNodeCollector; +import net.minecraft.client.renderer.rendertype.RenderTypes; +import net.minecraft.client.renderer.blockentity.state.BlockEntityRenderState; +import net.minecraft.client.renderer.feature.ModelFeatureRenderer; +import net.minecraft.client.renderer.state.CameraRenderState; +import net.minecraft.world.phys.Vec3; import com.mojang.math.Axis; import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider.Context; import net.minecraft.core.Direction; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import zone.moddev.mc.basesteam.SteamAdvantage; import zone.moddev.mc.basesteam.blocks.DrillBitTileEntity; @OnlyIn(Dist.CLIENT) -public class DrillBitRenderer implements BlockEntityRenderer { - private static final ResourceLocation TEXTURE = - ResourceLocation.fromNamespaceAndPath(SteamAdvantage.MODID, "textures/entity/drill_bit.png"); +public class DrillBitRenderer implements BlockEntityRenderer { + private static final Identifier TEXTURE = + Identifier.fromNamespaceAndPath(SteamAdvantage.MODID, "textures/entity/drill_bit.png"); public DrillBitRenderer(Context dispatcher) { } + public static final class State extends BlockEntityRenderState { + Direction.Axis axis = Direction.Axis.Y; + float rotation; + } + + @Override public State createRenderState() { return new State(); } + + @Override + public void extractRenderState(DrillBitTileEntity tile, State state, float partialTick, Vec3 camera, + ModelFeatureRenderer.CrumblingOverlay breaking) { + BlockEntityRenderer.super.extractRenderState(tile, state, partialTick, camera, breaking); + state.axis = tile.getDirection(); + state.rotation = tile.rotation + DrillBitTileEntity.ROTATION_PER_TICK * partialTick; + } + @Override - public void render(DrillBitTileEntity tile, float partialTick, PoseStack matrices, - MultiBufferSource buffers, int light, int overlay) { - renderGeometry(tile.getDirection(), tile.rotation + DrillBitTileEntity.ROTATION_PER_TICK * partialTick, - matrices, buffers.getBuffer(RenderType.entityTranslucent(TEXTURE)), light, overlay); + public void submit(State state, PoseStack matrices, SubmitNodeCollector collector, CameraRenderState camera) { + matrices.pushPose(); + try { + positionGeometry(state.axis, state.rotation, matrices); + int light = state.lightCoords; + collector.submitCustomGeometry(matrices, RenderTypes.entityTranslucent(TEXTURE), + (pose, vertices) -> renderQuads(pose, vertices, light, + net.minecraft.client.renderer.texture.OverlayTexture.NO_OVERLAY)); + } finally { + matrices.popPose(); + } } static void renderGeometry(Direction.Axis axis, float rotation, PoseStack matrices, VertexConsumer vertices, int light, int overlay) { matrices.pushPose(); try { - matrices.translate(0.5D, 0.5D, 0.5D); - if (axis != Direction.Axis.Y) { - matrices.mulPose(Axis.XP.rotationDegrees(90.0F)); - if (axis != Direction.Axis.Z) matrices.mulPose(Axis.ZP.rotationDegrees(90.0F)); - } - matrices.mulPose(Axis.YP.rotationDegrees(rotation)); - PoseStack.Pose transform = matrices.last(); + positionGeometry(axis, rotation, matrices); + renderQuads(matrices.last(), vertices, light, overlay); + } finally { + matrices.popPose(); + } + } + + private static void positionGeometry(Direction.Axis axis, float rotation, PoseStack matrices) { + matrices.translate(0.5D, 0.5D, 0.5D); + if (axis != Direction.Axis.Y) { + matrices.mulPose(Axis.XP.rotationDegrees(90.0F)); + if (axis != Direction.Axis.Z) matrices.mulPose(Axis.ZP.rotationDegrees(90.0F)); + } + matrices.mulPose(Axis.YP.rotationDegrees(rotation)); + } + + private static void renderQuads(PoseStack.Pose transform, VertexConsumer vertices, int light, int overlay) { final float radius = 0.25F; // Retain the original six quads and UV layout; native buffers supply lighting and overlay. vertex(transform, vertices, radius, 0.5F, -radius, 0, 0, 0, 0, -1, light, overlay); @@ -71,9 +107,6 @@ static void renderGeometry(Direction.Axis axis, float rotation, PoseStack matric vertex(transform, vertices, -radius, -0.5F, -radius, 0.5F, 0.5F, 0, -1, 0, light, overlay); vertex(transform, vertices, radius, -0.5F, -radius, 1, 0.5F, 0, -1, 0, light, overlay); vertex(transform, vertices, radius, -0.5F, radius, 1, 0, 0, -1, 0, light, overlay); - } finally { - matrices.popPose(); - } } private static void vertex(PoseStack.Pose transform, VertexConsumer vertices, diff --git a/src/main/java/zone/moddev/mc/basesteam/gui/BlastFurnaceGUI.java b/src/main/java/zone/moddev/mc/basesteam/gui/BlastFurnaceGUI.java index 499d7eb..a4b5dcd 100644 --- a/src/main/java/zone/moddev/mc/basesteam/gui/BlastFurnaceGUI.java +++ b/src/main/java/zone/moddev/mc/basesteam/gui/BlastFurnaceGUI.java @@ -3,7 +3,7 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import cyano.poweradvantage.api.simple.SimpleMachineGUI; import zone.moddev.mc.basepower.math.Integer2D; @@ -14,14 +14,14 @@ public class BlastFurnaceGUI extends SimpleMachineGUI{ public BlastFurnaceGUI() { super( - ResourceLocation.fromNamespaceAndPath(SteamAdvantage.MODID, "textures/gui/container/steam_furnace.png"), + Identifier.fromNamespaceAndPath(SteamAdvantage.MODID, "textures/gui/container/steam_furnace.png"), Integer2D.fromCoordinates(29,80, 93,19, 111,19, 129,19, 93,80, 111,80, 129,80) ); for(int i = 4; i < 7; i++)super.specialSlots.put(i, (SlotContext c)->new Slot(c.machineInventory,c.slotIndex,c.screenPositionX,c.screenPositionY){ @Override public void onTake(Player player, ItemStack stack) { - stack.onCraftedBy(player.level(), player, stack.getCount()); + stack.onCraftedBy(player, stack.getCount()); super.onTake(player, stack); } @Override diff --git a/src/main/java/zone/moddev/mc/basesteam/gui/CoalBoilerGUI.java b/src/main/java/zone/moddev/mc/basesteam/gui/CoalBoilerGUI.java index c781a83..68339d9 100644 --- a/src/main/java/zone/moddev/mc/basesteam/gui/CoalBoilerGUI.java +++ b/src/main/java/zone/moddev/mc/basesteam/gui/CoalBoilerGUI.java @@ -1,6 +1,6 @@ package zone.moddev.mc.basesteam.gui; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import cyano.poweradvantage.api.simple.SimpleMachineGUI; import zone.moddev.mc.basepower.math.Integer2D; @@ -11,7 +11,7 @@ public class CoalBoilerGUI extends SimpleMachineGUI{ public CoalBoilerGUI() { super( - ResourceLocation.fromNamespaceAndPath(SteamAdvantage.MODID, "textures/gui/container/coal_boiler.png"), + Identifier.fromNamespaceAndPath(SteamAdvantage.MODID, "textures/gui/container/coal_boiler.png"), new Integer2D[] {new Integer2D(80,80)} ); } diff --git a/src/main/java/zone/moddev/mc/basesteam/gui/ElectricBoilerGUI.java b/src/main/java/zone/moddev/mc/basesteam/gui/ElectricBoilerGUI.java index 4a205c0..1810810 100644 --- a/src/main/java/zone/moddev/mc/basesteam/gui/ElectricBoilerGUI.java +++ b/src/main/java/zone/moddev/mc/basesteam/gui/ElectricBoilerGUI.java @@ -1,6 +1,6 @@ package zone.moddev.mc.basesteam.gui; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import cyano.poweradvantage.api.simple.SimpleMachineGUI; import zone.moddev.mc.basepower.math.Integer2D; @@ -11,7 +11,7 @@ public class ElectricBoilerGUI extends SimpleMachineGUI{ public ElectricBoilerGUI() { super( - ResourceLocation.fromNamespaceAndPath(SteamAdvantage.MODID, "textures/gui/container/electric_boiler.png"), + Identifier.fromNamespaceAndPath(SteamAdvantage.MODID, "textures/gui/container/electric_boiler.png"), new Integer2D[0] ); } diff --git a/src/main/java/zone/moddev/mc/basesteam/gui/GUIHelper.java b/src/main/java/zone/moddev/mc/basesteam/gui/GUIHelper.java index d1f5976..80c2f9d 100644 --- a/src/main/java/zone/moddev/mc/basesteam/gui/GUIHelper.java +++ b/src/main/java/zone/moddev/mc/basesteam/gui/GUIHelper.java @@ -2,13 +2,8 @@ import cyano.poweradvantage.api.simple.SimpleMachineGUI.GUIContainer; import zone.moddev.mc.basepower.gui.FluidTankGUI; -import com.mojang.blaze3d.vertex.Tesselator; -import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.math.Axis; -import com.mojang.blaze3d.vertex.BufferBuilder; -import com.mojang.blaze3d.vertex.DefaultVertexFormat; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.Mth; +import org.joml.Matrix3x2fStack; +import net.minecraft.resources.Identifier; import cyano.poweradvantage.api.fluid.FluidStack; @net.minecraftforge.api.distmarker.OnlyIn(net.minecraftforge.api.distmarker.Dist.CLIENT) @@ -26,20 +21,20 @@ public static float maxDelta(float newValue,float oldValue,float maxChange){ } public static void drawNeedle(GUIContainer container, float pivotX, float pivotY, float zLevel, float value) { - PoseStack pose = container.getMatrices(); - pose.pushPose(); + Matrix3x2fStack pose = container.getMatrices(); + pose.pushMatrix(); try { - positionNeedle(pose, pivotX, pivotY, zLevel, value); + positionNeedle(pose, pivotX, pivotY, value); container.blit(0, 0, 176, 0, 7, 35); } finally { - pose.popPose(); + pose.popMatrix(); } } - static void positionNeedle(PoseStack pose, float pivotX, float pivotY, float zLevel, float value) { - pose.translate(pivotX, pivotY, zLevel); - pose.mulPose(Axis.ZP.rotationDegrees(needleRotation(value))); - pose.translate(-3.5F, -17.5F, 0.0F); + static void positionNeedle(Matrix3x2fStack pose, float pivotX, float pivotY, float value) { + pose.translate(pivotX, pivotY); + pose.rotate((float) Math.toRadians(needleRotation(value))); + pose.translate(-3.5F, -17.5F); } private static float needleRotation(float value) { @@ -50,65 +45,12 @@ private static float needleRotation(float value) { @Deprecated public static void drawNeedle(float pivotX, float pivotY, float zLevel, float value) { - value = displayLevel(value); - - - final float pixelSize = 1.0f / 256; // conversion factor - - final float PI = 3.141592654f; - final float needleX = 176 ; - final float needleW = 7 ; - final float needleY = 0 ; - final float needleH = 35; - final float needleL = needleH * 0.5f; - final float needleR = needleW * 0.5f; - - float angle = (1f - value) * PI; - float offsetX = pivotX; - float offsetY = pivotY; - - float sin = Mth.sin(angle); - float cos = Mth.cos(angle); - - float tipX = cos * needleL + offsetX; - float tipY = -sin * needleL + offsetY; - float baseX = -cos * needleL + offsetX; - float baseY = sin * needleL + offsetY; - float dx = -sin * needleR; - float dy = -cos * needleR; - - float[] x = new float[4]; - float[] y = new float[4]; - float[] u = new float[4]; - float[] v = new float[4]; - // top-left, bottom-left, bottom-right, top-right - x[0] = tipX - dx; - x[1] = baseX - dx; - x[2] = baseX + dx; - x[3] = tipX + dx; - y[0] = tipY - dy; - y[1] = baseY - dy; - y[2] = baseY + dy; - y[3] = tipY + dy; - u[0] = pixelSize * needleX; - u[1] = u[0]; - u[2] = u[0] + pixelSize * needleW; - u[3] = u[2]; - v[0] = pixelSize * needleY; - v[1] = v[0] + pixelSize * needleH; - v[2] = v[1]; - v[3] = v[0]; - - com.mojang.blaze3d.systems.RenderSystem.setShader(net.minecraft.client.renderer.GameRenderer::getPositionTexShader); - BufferBuilder renderer = Tesselator.getInstance().begin( - com.mojang.blaze3d.vertex.VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX); + drawNeedle(drawingContainer(net.minecraft.client.Minecraft.getInstance().screen), pivotX, pivotY, zLevel, value); + } - // (x, y, z, u, v) - for(int i = 3; i >= 0; i--){ - renderer.addVertex(x[i], y[i], zLevel).setUv(u[i],v[i]); - } - com.mojang.blaze3d.vertex.BufferUploader.drawWithShader(renderer.buildOrThrow()); - + static GUIContainer drawingContainer(net.minecraft.client.gui.screens.Screen screen) { + if (screen instanceof GUIContainer container && container.getGraphics() != null) return container; + throw new IllegalStateException("A needle requires an active machine decoration callback"); } public static void drawFlameProgress(int x, int y, float flameHeight, GUIContainer gc){ @@ -127,7 +69,7 @@ public static void drawFlameProgress(int x, int y, float flameHeight, GUIContain public static void drawFluidBar(FluidStack fs, float barHeight, int xPos, int yPos, GUIContainer guiContainer, int x, int y, float z, - ResourceLocation displayImage, int texOverlayX, int texOverlayY, int texOverlayW, int texOverlayH){ + Identifier displayImage, int texOverlayX, int texOverlayY, int texOverlayW, int texOverlayH){ barHeight = displayLevel(barHeight); final int texMarginW = (texOverlayW - 16) / 2; final int texMarginH = (texOverlayH - 60) / 2; diff --git a/src/main/java/zone/moddev/mc/basesteam/gui/GeothermalBoilerGUI.java b/src/main/java/zone/moddev/mc/basesteam/gui/GeothermalBoilerGUI.java index 8df9301..067458e 100644 --- a/src/main/java/zone/moddev/mc/basesteam/gui/GeothermalBoilerGUI.java +++ b/src/main/java/zone/moddev/mc/basesteam/gui/GeothermalBoilerGUI.java @@ -1,6 +1,6 @@ package zone.moddev.mc.basesteam.gui; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import cyano.poweradvantage.api.simple.SimpleMachineGUI; import zone.moddev.mc.basepower.math.Integer2D; @@ -11,7 +11,7 @@ public class GeothermalBoilerGUI extends SimpleMachineGUI{ public GeothermalBoilerGUI() { super( - ResourceLocation.fromNamespaceAndPath(SteamAdvantage.MODID, "textures/gui/container/geothermal_boiler.png"), + Identifier.fromNamespaceAndPath(SteamAdvantage.MODID, "textures/gui/container/geothermal_boiler.png"), new Integer2D[0] ); } diff --git a/src/main/java/zone/moddev/mc/basesteam/gui/OilBoilerGUI.java b/src/main/java/zone/moddev/mc/basesteam/gui/OilBoilerGUI.java index 5ba8fc6..042b938 100644 --- a/src/main/java/zone/moddev/mc/basesteam/gui/OilBoilerGUI.java +++ b/src/main/java/zone/moddev/mc/basesteam/gui/OilBoilerGUI.java @@ -5,14 +5,14 @@ import zone.moddev.mc.basepower.math.Integer2D; import zone.moddev.mc.basesteam.SteamAdvantage; import zone.moddev.mc.basesteam.machines.OilBoilerTileEntity; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import cyano.poweradvantage.api.fluid.FluidStack; public class OilBoilerGUI extends SimpleMachineGUI{ public OilBoilerGUI() { super( - ResourceLocation.fromNamespaceAndPath(SteamAdvantage.MODID, "textures/gui/container/oil_boiler.png"), + Identifier.fromNamespaceAndPath(SteamAdvantage.MODID, "textures/gui/container/oil_boiler.png"), new Integer2D[0] ); } diff --git a/src/main/java/zone/moddev/mc/basesteam/gui/RockCrusherGUI.java b/src/main/java/zone/moddev/mc/basesteam/gui/RockCrusherGUI.java index 68c0e25..8f44c70 100644 --- a/src/main/java/zone/moddev/mc/basesteam/gui/RockCrusherGUI.java +++ b/src/main/java/zone/moddev/mc/basesteam/gui/RockCrusherGUI.java @@ -1,6 +1,6 @@ package zone.moddev.mc.basesteam.gui; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import cyano.poweradvantage.api.simple.SimpleMachineGUI; import zone.moddev.mc.basepower.math.Integer2D; @@ -11,7 +11,7 @@ public class RockCrusherGUI extends SimpleMachineGUI{ public RockCrusherGUI() { super( - ResourceLocation.fromNamespaceAndPath(SteamAdvantage.MODID, "textures/gui/container/rock_crusher.png"), + Identifier.fromNamespaceAndPath(SteamAdvantage.MODID, "textures/gui/container/rock_crusher.png"), Integer2D.fromCoordinates(44,19, 44,88, 62,88, 80,88, 98,88, 116,88) ); } diff --git a/src/main/java/zone/moddev/mc/basesteam/gui/SteamDrillGUI.java b/src/main/java/zone/moddev/mc/basesteam/gui/SteamDrillGUI.java index 8c9b035..629b467 100644 --- a/src/main/java/zone/moddev/mc/basesteam/gui/SteamDrillGUI.java +++ b/src/main/java/zone/moddev/mc/basesteam/gui/SteamDrillGUI.java @@ -1,6 +1,6 @@ package zone.moddev.mc.basesteam.gui; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import cyano.poweradvantage.api.simple.SimpleMachineGUI; import zone.moddev.mc.basepower.math.Integer2D; @@ -11,7 +11,7 @@ public class SteamDrillGUI extends SimpleMachineGUI{ public SteamDrillGUI() { super( - ResourceLocation.fromNamespaceAndPath(SteamAdvantage.MODID, "textures/gui/container/steam_drill.png"), + Identifier.fromNamespaceAndPath(SteamAdvantage.MODID, "textures/gui/container/steam_drill.png"), Integer2D.fromCoordinates(44,88, 62,88, 80,88, 98,88, 116,88) ); } diff --git a/src/main/java/zone/moddev/mc/basesteam/gui/SteamPumpGUI.java b/src/main/java/zone/moddev/mc/basesteam/gui/SteamPumpGUI.java index 51efb16..53d6de4 100644 --- a/src/main/java/zone/moddev/mc/basesteam/gui/SteamPumpGUI.java +++ b/src/main/java/zone/moddev/mc/basesteam/gui/SteamPumpGUI.java @@ -5,14 +5,14 @@ import zone.moddev.mc.basepower.math.Integer2D; import zone.moddev.mc.basesteam.SteamAdvantage; import zone.moddev.mc.basesteam.machines.SteamPumpTileEntity; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import cyano.poweradvantage.api.fluid.FluidStack; public class SteamPumpGUI extends SimpleMachineGUI{ public SteamPumpGUI() { super( - ResourceLocation.fromNamespaceAndPath(SteamAdvantage.MODID, "textures/gui/container/steam_pump.png"), + Identifier.fromNamespaceAndPath(SteamAdvantage.MODID, "textures/gui/container/steam_pump.png"), new Integer2D[0] ); } diff --git a/src/main/java/zone/moddev/mc/basesteam/gui/SteamStillGUI.java b/src/main/java/zone/moddev/mc/basesteam/gui/SteamStillGUI.java index 2a9dbe5..18f667e 100644 --- a/src/main/java/zone/moddev/mc/basesteam/gui/SteamStillGUI.java +++ b/src/main/java/zone/moddev/mc/basesteam/gui/SteamStillGUI.java @@ -5,14 +5,14 @@ import zone.moddev.mc.basepower.math.Integer2D; import zone.moddev.mc.basesteam.SteamAdvantage; import zone.moddev.mc.basesteam.machines.SteamStillTileEntity; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import cyano.poweradvantage.api.fluid.FluidStack; public class SteamStillGUI extends SimpleMachineGUI{ public SteamStillGUI() { super( - ResourceLocation.fromNamespaceAndPath(SteamAdvantage.MODID, "textures/gui/container/steam_still.png"), + Identifier.fromNamespaceAndPath(SteamAdvantage.MODID, "textures/gui/container/steam_still.png"), new Integer2D[0] ); } diff --git a/src/main/java/zone/moddev/mc/basesteam/gui/SteamTankGUI.java b/src/main/java/zone/moddev/mc/basesteam/gui/SteamTankGUI.java index 21f2a4b..7ec65cc 100644 --- a/src/main/java/zone/moddev/mc/basesteam/gui/SteamTankGUI.java +++ b/src/main/java/zone/moddev/mc/basesteam/gui/SteamTankGUI.java @@ -1,6 +1,6 @@ package zone.moddev.mc.basesteam.gui; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import cyano.poweradvantage.api.simple.SimpleMachineGUI; import zone.moddev.mc.basepower.math.Integer2D; @@ -11,7 +11,7 @@ public class SteamTankGUI extends SimpleMachineGUI{ public SteamTankGUI() { super( - ResourceLocation.fromNamespaceAndPath(SteamAdvantage.MODID, "textures/gui/container/steam_tank.png"), + Identifier.fromNamespaceAndPath(SteamAdvantage.MODID, "textures/gui/container/steam_tank.png"), new Integer2D[0] ); } diff --git a/src/main/java/zone/moddev/mc/basesteam/init/Blocks.java b/src/main/java/zone/moddev/mc/basesteam/init/Blocks.java index 8aa0ade..4c52316 100644 --- a/src/main/java/zone/moddev/mc/basesteam/init/Blocks.java +++ b/src/main/java/zone/moddev/mc/basesteam/init/Blocks.java @@ -8,8 +8,8 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.item.Item; import net.minecraft.world.item.BlockItem; -import net.minecraft.resources.ResourceLocation; -import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraft.resources.Identifier; +import net.minecraftforge.eventbus.api.listener.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.RegisterEvent; @@ -78,7 +78,8 @@ public static synchronized void init() { steam_elevator_platforms[i] = steam_elevator_platform.defaultBlockState().setValue(PlatformBlock.HEIGHT, i); } steam_elevator = (GUIBlock) add(new SteamElevatorBlock(), "steam_elevator"); - steam_switch = add(new BlockPowerSwitch(Power.steam_power), "steam_switch"); + steam_switch = add(new BlockPowerSwitch(Power.steam_power, + zone.moddev.mc.basesteam.blocks.SteamBlockProperties.pistonLike("steam_switch")), "steam_switch"); pump_pipe_steam = add(new PumpPipeBlock(), "pump_pipe_steam"); steam_still = (GUIBlock) add(new SteamStillBlock(), "steam_still"); steam_pump = (GUIBlock) add(new SteamPumpBlock(), "steam_pump"); @@ -96,14 +97,16 @@ public static void registerBlocks(RegisterEvent event) { if (!ForgeRegistries.Keys.BLOCKS.equals(event.getRegistryKey())) return; init(); event.register(ForgeRegistries.Keys.BLOCKS, registry -> - ALL_BLOCKS.forEach((id, block) -> registry.register(ResourceLocation.fromNamespaceAndPath(SteamAdvantage.MODID, id), block))); + ALL_BLOCKS.forEach((id, block) -> registry.register(Identifier.fromNamespaceAndPath(SteamAdvantage.MODID, id), block))); } @SubscribeEvent public static void registerBlockItems(RegisterEvent event) { event.register(ForgeRegistries.Keys.ITEMS, registry -> ALL_BLOCKS.forEach((id, block) -> { - BlockItem item = new BlockItem(block, new Item.Properties()); - registry.register(ResourceLocation.fromNamespaceAndPath(SteamAdvantage.MODID, id), item); + BlockItem item = new BlockItem(block, new Item.Properties().setId( + net.minecraft.resources.ResourceKey.create(net.minecraft.core.registries.Registries.ITEM, + Identifier.fromNamespaceAndPath(SteamAdvantage.MODID, id))).useBlockDescriptionPrefix()); + registry.register(Identifier.fromNamespaceAndPath(SteamAdvantage.MODID, id), item); if (!"pump_pipe_steam".equals(id) && !"drillbit".equals(id)) ItemGroups.show(item); })); } diff --git a/src/main/java/zone/moddev/mc/basesteam/init/Enchantments.java b/src/main/java/zone/moddev/mc/basesteam/init/Enchantments.java index 8bec0b5..848ae15 100644 --- a/src/main/java/zone/moddev/mc/basesteam/init/Enchantments.java +++ b/src/main/java/zone/moddev/mc/basesteam/init/Enchantments.java @@ -7,7 +7,7 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.enchantment.Enchantment; import net.minecraft.world.item.enchantment.EnchantmentHelper; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; /** Stable save identities; holders belong to the current world's data-driven registry. */ public final class Enchantments { @@ -20,11 +20,11 @@ private Enchantments() {} public static int level(ResourceKey enchantment, ItemStack stack, RegistryAccess registries) { return EnchantmentHelper.getItemEnchantmentLevel( - registries.registryOrThrow(Registries.ENCHANTMENT).getHolderOrThrow(enchantment), stack); + registries.lookupOrThrow(Registries.ENCHANTMENT).getOrThrow(enchantment), stack); } private static ResourceKey key(String path) { return ResourceKey.create(Registries.ENCHANTMENT, - ResourceLocation.fromNamespaceAndPath(SteamAdvantage.MODID, path)); + Identifier.fromNamespaceAndPath(SteamAdvantage.MODID, path)); } } diff --git a/src/main/java/zone/moddev/mc/basesteam/init/Entities.java b/src/main/java/zone/moddev/mc/basesteam/init/Entities.java index 78cb371..e287de4 100644 --- a/src/main/java/zone/moddev/mc/basesteam/init/Entities.java +++ b/src/main/java/zone/moddev/mc/basesteam/init/Entities.java @@ -4,8 +4,8 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.Block; -import net.minecraft.resources.ResourceLocation; -import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraft.resources.Identifier; +import net.minecraftforge.eventbus.api.listener.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.RegisterEvent; @@ -61,8 +61,8 @@ public static void register(RegisterEvent event) { private static BlockEntityType register( RegisterEvent event, String id, BlockEntityType.BlockEntitySupplier factory, Block block) { - BlockEntityType type = BlockEntityType.Builder.of(factory, block).build(null); - event.register(ForgeRegistries.Keys.BLOCK_ENTITY_TYPES, ResourceLocation.fromNamespaceAndPath(SteamAdvantage.MODID, id), () -> type); + BlockEntityType type = new BlockEntityType<>(factory, java.util.Set.of(block)); + event.register(ForgeRegistries.Keys.BLOCK_ENTITY_TYPES, Identifier.fromNamespaceAndPath(SteamAdvantage.MODID, id), () -> type); return type; } } diff --git a/src/main/java/zone/moddev/mc/basesteam/init/Items.java b/src/main/java/zone/moddev/mc/basesteam/init/Items.java index 846f63b..09d1400 100644 --- a/src/main/java/zone/moddev/mc/basesteam/init/Items.java +++ b/src/main/java/zone/moddev/mc/basesteam/init/Items.java @@ -4,9 +4,9 @@ import java.util.Map; import java.util.Set; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.item.Item; -import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.eventbus.api.listener.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.RegisterEvent; @@ -28,10 +28,10 @@ private Items() {} /** Constructs items during the Forge item registry event. */ public static synchronized void init() { if (initialized) return; - steam_governor = add("steam_governor", new Item(new Item.Properties())); - steam_drill_bit = add("steam_drill_bit", new Item(new Item.Properties())); - blackpowder_cartridge = add("blackpowder_cartridge", new Item(new Item.Properties())); - blackpowder_musket = add("musket", new MusketItem(new Item.Properties().stacksTo(1))); + steam_governor = add("steam_governor", new Item(properties("steam_governor"))); + steam_drill_bit = add("steam_drill_bit", new Item(properties("steam_drill_bit"))); + blackpowder_cartridge = add("blackpowder_cartridge", new Item(properties("blackpowder_cartridge"))); + blackpowder_musket = add("musket", new MusketItem(properties("musket").stacksTo(1))); initialized = true; } @@ -40,12 +40,18 @@ private static Item add(String id, Item item) { return item; } + private static Item.Properties properties(String path) { + return new Item.Properties().setId(net.minecraft.resources.ResourceKey.create( + net.minecraft.core.registries.Registries.ITEM, + Identifier.fromNamespaceAndPath(SteamAdvantage.MODID, path))); + } + @SubscribeEvent public static void register(RegisterEvent event) { if (!ForgeRegistries.Keys.ITEMS.equals(event.getRegistryKey())) return; init(); event.register(ForgeRegistries.Keys.ITEMS, registry -> allItems.forEach((id, item) -> { - registry.register(ResourceLocation.fromNamespaceAndPath(SteamAdvantage.MODID, id), item); + registry.register(Identifier.fromNamespaceAndPath(SteamAdvantage.MODID, id), item); ItemGroups.show(item); })); } diff --git a/src/main/java/zone/moddev/mc/basesteam/init/Power.java b/src/main/java/zone/moddev/mc/basesteam/init/Power.java index 5a6d3ce..0a69527 100644 --- a/src/main/java/zone/moddev/mc/basesteam/init/Power.java +++ b/src/main/java/zone/moddev/mc/basesteam/init/Power.java @@ -4,7 +4,7 @@ import net.minecraft.world.damagesource.DamageType; import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.level.Level; import cyano.poweradvantage.api.ConduitType; import zone.moddev.mc.basesteam.SteamAdvantage; @@ -28,10 +28,10 @@ public static DamageSource musketDamage(Level level) { } private static ResourceKey damageType(String id) { - return ResourceKey.create(Registries.DAMAGE_TYPE, ResourceLocation.fromNamespaceAndPath(SteamAdvantage.MODID, id)); + return ResourceKey.create(Registries.DAMAGE_TYPE, Identifier.fromNamespaceAndPath(SteamAdvantage.MODID, id)); } private static DamageSource damageSource(Level level, ResourceKey type) { - return new DamageSource(level.registryAccess().registryOrThrow(Registries.DAMAGE_TYPE).getHolderOrThrow(type)); + return new DamageSource(level.registryAccess().lookupOrThrow(Registries.DAMAGE_TYPE).getOrThrow(type)); } } diff --git a/src/main/java/zone/moddev/mc/basesteam/init/Villages.java b/src/main/java/zone/moddev/mc/basesteam/init/Villages.java index 638734d..0f9995b 100644 --- a/src/main/java/zone/moddev/mc/basesteam/init/Villages.java +++ b/src/main/java/zone/moddev/mc/basesteam/init/Villages.java @@ -5,14 +5,14 @@ import net.minecraft.tags.EnchantmentTags; import net.minecraft.util.RandomSource; import net.minecraft.world.item.enchantment.EnchantmentHelper; -import net.minecraft.world.entity.npc.VillagerProfession; -import net.minecraft.world.entity.npc.VillagerTrades; +import net.minecraft.world.entity.npc.villager.VillagerProfession; +import net.minecraft.world.entity.npc.villager.VillagerTrades; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.trading.MerchantOffer; import net.minecraft.world.item.trading.ItemCost; import net.minecraftforge.event.village.VillagerTradesEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.eventbus.api.listener.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import zone.moddev.mc.basesteam.SteamAdvantage; @@ -24,7 +24,7 @@ public static void init() {} @SubscribeEvent public static void addTrades(VillagerTradesEvent event) { - VillagerProfession profession = event.getType(); + net.minecraft.resources.ResourceKey profession = event.getType(); List novice = event.getTrades().get(1); if (profession == VillagerProfession.ARMORER || profession == VillagerProfession.TOOLSMITH || profession == VillagerProfession.WEAPONSMITH || profession == VillagerProfession.LIBRARIAN) { @@ -36,12 +36,12 @@ public static void addTrades(VillagerTradesEvent event) { novice.add(sell(Items.blackpowder_musket, 10, 15)); } if (profession == VillagerProfession.CLERIC) { - event.getTrades().get(3).add((trader, random) -> { + event.getTrades().get(3).add((level, trader, random) -> { int enchantmentLevel = 5 + random.nextInt(15); ItemStack musket = EnchantmentHelper.enchantItem(random, new ItemStack(Items.blackpowder_musket), enchantmentLevel, - trader.registryAccess().registryOrThrow(Registries.ENCHANTMENT) - .getTag(EnchantmentTags.NON_TREASURE).orElseThrow().stream()); + trader.registryAccess().lookupOrThrow(Registries.ENCHANTMENT) + .getOrThrow(EnchantmentTags.NON_TREASURE).stream()); int price = Math.min(randomPrice(random, 15, 25) + enchantmentLevel, 64); return offer(price, musket); }); @@ -49,7 +49,7 @@ public static void addTrades(VillagerTradesEvent event) { } private static VillagerTrades.ItemListing sell(Item item, int min, int max) { - return (trader, random) -> { + return (level, trader, random) -> { int price = randomPrice(random, min, max); return offer(price < 0 ? 1 : price, new ItemStack(item, price < 0 ? -price : 1)); }; diff --git a/src/main/java/zone/moddev/mc/basesteam/items/MusketItem.java b/src/main/java/zone/moddev/mc/basesteam/items/MusketItem.java index 38ffccc..42a683b 100644 --- a/src/main/java/zone/moddev/mc/basesteam/items/MusketItem.java +++ b/src/main/java/zone/moddev/mc/basesteam/items/MusketItem.java @@ -24,7 +24,7 @@ import net.minecraft.sounds.SoundEvents; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.EquipmentSlotGroup; -import net.minecraft.world.item.UseAnim; +import net.minecraft.world.item.ItemUseAnimation; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Item; import net.minecraft.world.item.component.CustomData; @@ -32,10 +32,10 @@ import net.minecraft.core.particles.ParticleTypes; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket; -import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.InteractionResult; import net.minecraft.world.InteractionHand; import net.minecraft.core.particles.ParticleOptions; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.sounds.SoundSource; import net.minecraft.sounds.SoundEvent; import net.minecraft.world.phys.AABB; @@ -56,18 +56,18 @@ @SuppressWarnings("deprecation") public class MusketItem extends net.minecraft.world.item.Item{ private static final TagKey FRAGILE_TARGETS = TagKey.create(Registries.BLOCK, - ResourceLocation.fromNamespaceAndPath(SteamAdvantage.MODID, "musket_fragile")); + Identifier.fromNamespaceAndPath(SteamAdvantage.MODID, "musket_fragile")); public static final String NBT_DATA_KEY_LOADED = "loaded"; public static final double MAX_RANGE = 64; private static final int maxUseTime = 7200; - private static final TagKey BLACKPOWDER_AMMO_TAG = ItemTags.create(ResourceLocation.fromNamespaceAndPath("forge", "ammo/blackpowder")); - private static final TagKey STEEL_INGOT_TAG = ItemTags.create(ResourceLocation.fromNamespaceAndPath("forge", "ingots/steel")); + private static final TagKey BLACKPOWDER_AMMO_TAG = ItemTags.create(Identifier.fromNamespaceAndPath("forge", "ammo/blackpowder")); + private static final TagKey STEEL_INGOT_TAG = ItemTags.create(Identifier.fromNamespaceAndPath("forge", "ingots/steel")); public MusketItem(Item.Properties properties){ - super(properties.durability(250).attributes(ItemAttributeModifiers.builder() + super(properties.durability(250).enchantable(1).repairable(STEEL_INGOT_TAG).attributes(ItemAttributeModifiers.builder() .add(Attributes.ATTACK_DAMAGE, new AttributeModifier(BASE_ATTACK_DAMAGE_ID, getMeleeDamage(), AttributeModifier.Operation.ADD_VALUE), EquipmentSlotGroup.MAINHAND) @@ -86,39 +86,34 @@ public static float getMeleeDamage(){ } @Override - public int getEnchantmentValue(){ - return 1; - } - - @Override - public UseAnim getUseAnimation(ItemStack stack){ + public ItemUseAnimation getUseAnimation(ItemStack stack){ if(isLoaded(stack)){ - return UseAnim.BOW; + return ItemUseAnimation.BOW; } else { - return UseAnim.BLOCK; + return ItemUseAnimation.BLOCK; } } @Override - public InteractionResultHolder use(Level world, Player playerEntity, InteractionHand hand){ + public InteractionResult use(Level world, Player playerEntity, InteractionHand hand){ ItemStack srcItemStack = playerEntity.getItemInHand(hand); - if(hand != InteractionHand.MAIN_HAND) return InteractionResultHolder.pass(srcItemStack); - if(isNotLoaded(srcItemStack) && !hasAmmo(playerEntity, srcItemStack)) return InteractionResultHolder.fail(srcItemStack); + if(hand != InteractionHand.MAIN_HAND) return InteractionResult.PASS; + if(isNotLoaded(srcItemStack) && !hasAmmo(playerEntity, srcItemStack)) return InteractionResult.FAIL; if(isNotLoaded(srcItemStack) && Enchantments.level(Enchantments.rapid_reload, srcItemStack, world.registryAccess()) > 0){ // instant reload decrementAmmo(playerEntity,srcItemStack); startLoad(srcItemStack); playSound(SoundEvents.LEVER_CLICK,world,playerEntity); finishLoad(srcItemStack); - return InteractionResultHolder.success(srcItemStack); + return InteractionResult.SUCCESS; } playerEntity.startUsingItem(hand); - return InteractionResultHolder.success(srcItemStack); + return InteractionResult.SUCCESS; } @Override public void onUseTick(Level level, LivingEntity player, ItemStack stack, int count){ - if(level.isClientSide && count % 7 == 3 && isNotLoaded(stack)){ + if(level.isClientSide() && count % 7 == 3 && isNotLoaded(stack)){ // indicator to player that the gun is loading player.playSound(SoundType.STONE.getStepSound(), 0.5f, 1.0f); } @@ -139,12 +134,13 @@ public ItemStack finishUsingItem (ItemStack srcItemStack, Level world, LivingEnt } @Override - public void releaseUsing(ItemStack stack, Level worldIn, LivingEntity playerIn, int timeLeft) { + public boolean releaseUsing(ItemStack stack, Level worldIn, LivingEntity playerIn, int timeLeft) { if(isAlmostLoaded(stack)){ finishLoad(stack); } else if(isLoaded(stack)){ fire(stack,playerIn,worldIn); } + return true; } protected void fire(ItemStack srcItemStack, LivingEntity playerEntity, @@ -159,7 +155,7 @@ protected void fire(ItemStack srcItemStack, LivingEntity playerEntity, Vec3 lookVector = playerEntity.getLookAngle(); spawnParticle(ParticleTypes.EXPLOSION_EMITTER, world, playerEntity.getX()+lookVector.x, playerEntity.getY()+playerEntity.getEyeHeight()+lookVector.y, playerEntity.getZ() + lookVector.z); - if(world.isClientSide){ + if(world.isClientSide()){ playerEntity.setXRot(playerEntity.getXRot() - 15); return; } @@ -178,7 +174,7 @@ protected void fire(ItemStack srcItemStack, LivingEntity playerEntity, final Level.ExplosionInteraction explosionInteraction = Level.ExplosionInteraction.TNT; if(rayTrace instanceof EntityHitResult){ Entity e = ((EntityHitResult) rayTrace).getEntity(); - e.hurt(Power.musketDamage(world), getShotDamage()); + e.hurtServer((ServerLevel)world, Power.musketDamage(world), getShotDamage()); if(explosive > 0){ int lvl = explosive; world.explode(playerEntity, e.getX(), e.getY()+0.5, e.getZ(), @@ -200,8 +196,9 @@ protected void fire(ItemStack srcItemStack, LivingEntity playerEntity, if (bs.is(FRAGILE_TARGETS)) { world.destroyBlock(coord, true); } else if (bs.getBlock() instanceof TntBlock) { - TntBlock.explode(world, coord); - world.removeBlock(coord, false); + if (bs.getBlock().onCaughtFire(bs, world, coord, null, null)) { + world.removeBlock(coord, false); + } } } if(explosive > 0){ @@ -248,15 +245,14 @@ private void spawnParticle(ParticleOptions p, Level w, double x, double y, doubl } @Override - public boolean hurtEnemy(ItemStack stack, LivingEntity target, LivingEntity attacker) + public void hurtEnemy(ItemStack stack, LivingEntity target, LivingEntity attacker) { stack.hurtAndBreak(1, attacker, EquipmentSlot.MAINHAND); - return true; } /** plays a sound at the player location */ protected void playSound(SoundEvent sound, Level world, Entity e){ - if (!world.isClientSide) + if (!world.isClientSide()) { playSoundAtPosition(e.getX(), e.getY(), e.getZ(), sound,SoundSource.PLAYERS,1F,1F,world); } @@ -280,7 +276,7 @@ private static void setLoadedState(ItemStack musket, byte state) { private static byte loadedState(ItemStack musket) { CustomData data = musket.get(DataComponents.CUSTOM_DATA); - return data == null ? 0 : data.copyTag().getByte(NBT_DATA_KEY_LOADED); + return data == null ? 0 : data.copyTag().getByteOr(NBT_DATA_KEY_LOADED, (byte)0); } public static boolean isLoaded(ItemStack musket){ @@ -304,8 +300,8 @@ public static boolean hasAmmo(LivingEntity e, ItemStack musket) { if (playerEntity.isCreative() || Enchantments.level(net.minecraft.world.item.enchantment.Enchantments.INFINITY, musket, e.registryAccess()) > 0 || Enchantments.level(Enchantments.powderless, musket, e.registryAccess()) > 0) return true; - for (int i = 0; i < playerEntity.getInventory().items.size(); i++) { - ItemStack inventoryStack = playerEntity.getInventory().items.get(i); + for (int i = 0; i < playerEntity.getInventory().getNonEquipmentItems().size(); i++) { + ItemStack inventoryStack = playerEntity.getInventory().getItem(i); if (inventoryStack.isEmpty()) continue; if (inventoryStack.is(BLACKPOWDER_AMMO_TAG)) return true; } @@ -320,12 +316,12 @@ public static void decrementAmmo(LivingEntity e, ItemStack musket) { if (playerEntity.isCreative() || Enchantments.level(net.minecraft.world.item.enchantment.Enchantments.INFINITY, musket, e.registryAccess()) > 0 || Enchantments.level(Enchantments.powderless, musket, e.registryAccess()) > 0) return; - for (int i = 0; i < playerEntity.getInventory().items.size(); i++) { - ItemStack inventoryStack = playerEntity.getInventory().items.get(i); + for (int i = 0; i < playerEntity.getInventory().getNonEquipmentItems().size(); i++) { + ItemStack inventoryStack = playerEntity.getInventory().getItem(i); if (inventoryStack.isEmpty()) continue; if (inventoryStack.is(BLACKPOWDER_AMMO_TAG)) { inventoryStack.shrink(1); - if (inventoryStack.isEmpty()) playerEntity.getInventory().items.set(i, ItemStack.EMPTY); + if (inventoryStack.isEmpty()) playerEntity.getInventory().setItem(i, ItemStack.EMPTY); return; } } @@ -338,7 +334,7 @@ public static HitResult rayTraceBlocksAndEntities(Level w, double maxRange, Livi Vec3 rayDirection = source.getLookAngle(); Vec3 loadedEnd = loadedRayEnd(rayOrigin, rayOrigin.add(rayDirection.scale(maxRange)), w::hasChunkAt); if(loadedEnd.equals(rayOrigin)) return BlockHitResult.miss(rayOrigin, - net.minecraft.core.Direction.getNearest(rayDirection.x, rayDirection.y, rayDirection.z), BlockPos.containing(rayOrigin)); + net.minecraft.core.Direction.getApproximateNearest(rayDirection.x, rayDirection.y, rayDirection.z), BlockPos.containing(rayOrigin)); BlockHitResult terrainHit = w.clip(new ClipContext(rayOrigin, loadedEnd, ClipContext.Block.COLLIDER, ClipContext.Fluid.SOURCE_ONLY, source)); Vec3 rayEnd = terrainHit.getLocation(); @@ -416,24 +412,17 @@ private static double min(double a, double b){ return Math.min(a, b); } - /** - * Return whether this item is repairable in an anvil. - */ - @Override public boolean isValidRepairItem(ItemStack srcItemStack, ItemStack rawMaterial) - { - // repair with steel ingots - return rawMaterial.is(STEEL_INGOT_TAG); - } - @Override - public void appendHoverText(ItemStack stack, TooltipContext context, List list, net.minecraft.world.item.TooltipFlag flag){ - super.appendHoverText(stack,context,list,flag); - list.add(Component.translatable("tooltip.musket.damage", (int)MusketItem.getShotDamage())); + public void appendHoverText(ItemStack stack, TooltipContext context, + net.minecraft.world.item.component.TooltipDisplay display, java.util.function.Consumer list, + net.minecraft.world.item.TooltipFlag flag){ + super.appendHoverText(stack,context,display,list,flag); + list.accept(Component.translatable("tooltip.musket.damage", (int)MusketItem.getShotDamage())); if(isLoaded(stack)){ - list.add(Component.translatable("tooltip.musket.loaded")); + list.accept(Component.translatable("tooltip.musket.loaded")); } else { - list.add(Component.translatable("tooltip.musket.unloaded")); - list.add(Component.translatable("tooltip.musket.ammo", Component.translatable("item.basesteam.blackpowder_cartridge"))); + list.accept(Component.translatable("tooltip.musket.unloaded")); + list.accept(Component.translatable("tooltip.musket.ammo", Component.translatable("item.basesteam.blackpowder_cartridge"))); } } diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/BlastFurnaceBlock.java b/src/main/java/zone/moddev/mc/basesteam/machines/BlastFurnaceBlock.java index 8b3c39f..cee1f85 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/BlastFurnaceBlock.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/BlastFurnaceBlock.java @@ -16,7 +16,7 @@ public class BlastFurnaceBlock extends cyano.poweradvantage.api.simple.BlockSimp public BlastFurnaceBlock() { - super(machineProperties(0.75F), Power.steam_power); + super(machineProperties(0.75F).setId(zone.moddev.mc.basesteam.blocks.SteamBlockProperties.key("steam_furnace")), Power.steam_power); } @Override diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/BlastFurnaceTileEntity.java b/src/main/java/zone/moddev/mc/basesteam/machines/BlastFurnaceTileEntity.java index 8622d8c..7172f18 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/BlastFurnaceTileEntity.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/BlastFurnaceTileEntity.java @@ -2,7 +2,8 @@ import cyano.poweradvantage.api.ConduitType; import zone.moddev.mc.basesteam.init.Power; -import net.minecraft.core.HolderLookup; +import net.minecraft.world.level.storage.ValueInput; +import net.minecraft.world.level.storage.ValueOutput; import net.minecraft.world.entity.player.Player; import net.minecraft.sounds.SoundEvents; import net.minecraft.world.item.ItemStack; @@ -203,12 +204,12 @@ private void energyDecay() { private int getFuelBurnTime(ItemStack item) { if(item == null) return 0; - return zone.moddev.mc.basesteam.util.FurnaceFuelHelper.getBurnTime(item); + return zone.moddev.mc.basesteam.util.FurnaceFuelHelper.getBurnTime(item, getLevel()); } private void decrementFuel() { if(inventory[0].getCount() == 1){ - inventory[0] = inventory[0].getCraftingRemainingItem(); + inventory[0] = inventory[0].getCraftingRemainder(); } else { this.removeItem(0, 1); } @@ -268,17 +269,17 @@ public void onDataFieldUpdate() { @Override - protected void saveAdditional(CompoundTag tagRoot, HolderLookup.Provider registries){ - super.saveAdditional(tagRoot, registries); + protected void saveAdditional(ValueOutput tagRoot){ + super.saveAdditional(tagRoot); prepareDataFieldsForSync(); tagRoot.putIntArray("Data", getDataFieldArray()); } @Override - protected void loadAdditional(CompoundTag tagRoot, HolderLookup.Provider registries){ - super.loadAdditional(tagRoot, registries); - if(tagRoot.contains("Data")){ - int[] data = tagRoot.getIntArray("Data"); + protected void loadAdditional(ValueInput tagRoot){ + super.loadAdditional(tagRoot); + if(tagRoot.read("Data", com.mojang.serialization.Codec.INT_STREAM).isPresent()){ + int[] data = tagRoot.getIntArray("Data").orElseGet(() -> new int[0]); System.arraycopy(data, 0, this.getDataFieldArray(), 0, Math.min(data.length,this.getDataFieldArray().length)); onDataFieldUpdate(); } @@ -332,9 +333,10 @@ public boolean canPlaceItem(final int slot, final ItemStack item) { } private ItemStack getSmeltingResult(ItemStack input) { - if (input == null || input.isEmpty() || getLevel() == null) return ItemStack.EMPTY; + if (input == null || input.isEmpty() + || !(getLevel() instanceof net.minecraft.server.level.ServerLevel server)) return ItemStack.EMPTY; SingleRecipeInput inventory = new SingleRecipeInput(input); - return getLevel().getRecipeManager().getRecipeFor(RecipeType.SMELTING, inventory, getLevel()) + return server.recipeAccess().getRecipeFor(RecipeType.SMELTING, inventory, server) .map(recipe -> recipe.value().assemble(inventory, getLevel().registryAccess())).orElse(ItemStack.EMPTY); } diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/CoalBoilerBlock.java b/src/main/java/zone/moddev/mc/basesteam/machines/CoalBoilerBlock.java index 64d02a1..2b6a8e1 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/CoalBoilerBlock.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/CoalBoilerBlock.java @@ -21,7 +21,7 @@ public class CoalBoilerBlock extends cyano.poweradvantage.api.simple.BlockSimple public CoalBoilerBlock() { - super(machineProperties(0.75F), Power.steam_power, Fluids.fluidConduit_general); + super(machineProperties(0.75F).setId(zone.moddev.mc.basesteam.blocks.SteamBlockProperties.key("steam_boiler_coal")), Power.steam_power, Fluids.fluidConduit_general); } @Override diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/CoalBoilerTileEntity.java b/src/main/java/zone/moddev/mc/basesteam/machines/CoalBoilerTileEntity.java index ab01420..52bf177 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/CoalBoilerTileEntity.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/CoalBoilerTileEntity.java @@ -7,7 +7,8 @@ import cyano.poweradvantage.api.fluid.FluidRequest; import zone.moddev.mc.basepower.init.Fluids; import zone.moddev.mc.basesteam.init.Power; -import net.minecraft.core.HolderLookup; +import net.minecraft.world.level.storage.ValueInput; +import net.minecraft.world.level.storage.ValueOutput; import net.minecraft.world.entity.player.Player; import net.minecraft.sounds.SoundEvents; import net.minecraft.world.item.ItemStack; @@ -49,7 +50,7 @@ public CoalBoilerTileEntity(net.minecraft.core.BlockPos pos, net.minecraft.world @Override protected void onContentsChanged() { if (CoalBoilerTileEntity.this.getLevel() != null - && !CoalBoilerTileEntity.this.getLevel().isClientSide) { + && !CoalBoilerTileEntity.this.getLevel().isClientSide()) { CoalBoilerTileEntity.this.setChanged(); } } @@ -111,13 +112,13 @@ private void energyDecay() { private int getFuelBurnTime() { if(inventory[0] == null) return 0; - return zone.moddev.mc.basesteam.util.FurnaceFuelHelper.getBurnTime(inventory[0]); + return zone.moddev.mc.basesteam.util.FurnaceFuelHelper.getBurnTime(inventory[0], getLevel()); } private void decrementFuel() { if(inventory[0].getCount() == 1){ - inventory[0] = inventory[0].getCraftingRemainingItem(); + inventory[0] = inventory[0].getCraftingRemainder(); } else { this.removeItem(0, 1); } @@ -215,11 +216,11 @@ public void onDataFieldUpdate() { * @param tagRoot An NBT tag */ @Override - protected void saveAdditional(final CompoundTag tagRoot, HolderLookup.Provider registries) { - super.saveAdditional(tagRoot, registries); + protected void saveAdditional(ValueOutput tagRoot) { + super.saveAdditional(tagRoot); CompoundTag tankTag = new CompoundTag(); this.getTank().writeToNBT(tankTag); - tagRoot.put("Tank", tankTag); + tagRoot.store("Tank", CompoundTag.CODEC, tankTag); if(this.burnTime > 0)tagRoot.putInt("BurnTime", this.burnTime); if(this.totalBurnTime > 0)tagRoot.putInt("BurnTimeTotal", this.totalBurnTime); } @@ -228,23 +229,23 @@ protected void saveAdditional(final CompoundTag tagRoot, HolderLookup.Provider r * @param tagRoot An NBT tag */ @Override - protected void loadAdditional(final CompoundTag tagRoot, HolderLookup.Provider registries) { - super.loadAdditional(tagRoot, registries); - if (tagRoot.contains("Tank")) { - CompoundTag tankTag = tagRoot.getCompound("Tank"); + protected void loadAdditional(ValueInput tagRoot) { + super.loadAdditional(tagRoot); + if (tagRoot.read("Tank", CompoundTag.CODEC).isPresent()) { + CompoundTag tankTag = tagRoot.read("Tank", CompoundTag.CODEC).orElseGet(CompoundTag::new); getTank().readFromNBT(tankTag); if(tankTag.contains("Empty")){ // empty the tank if NBT says its empty (not default behavior of Tank.read(...) ) getTank().setFluid(null); } } - if(tagRoot.contains("BurnTime")){ - this.burnTime = tagRoot.getInt("BurnTime"); + if(tagRoot.read("BurnTime", com.mojang.serialization.Codec.INT).isPresent()){ + this.burnTime = tagRoot.getIntOr("BurnTime", 0); } else { this.burnTime = 0; } - if(tagRoot.contains("BurnTimeTotal")){ - this.totalBurnTime = tagRoot.getInt("BurnTimeTotal"); + if(tagRoot.read("BurnTimeTotal", com.mojang.serialization.Codec.INT).isPresent()){ + this.totalBurnTime = tagRoot.getIntOr("BurnTimeTotal", 0); } else { this.totalBurnTime = 0; } @@ -438,7 +439,7 @@ public FluidTankInfo[] getTankInfo(Direction face) { @Override public boolean canPlaceItem(final int slot, final ItemStack item) { - return super.canPlaceItem(slot, item) && zone.moddev.mc.basesteam.util.FurnaceFuelHelper.getBurnTime(item) > 0; + return super.canPlaceItem(slot, item) && zone.moddev.mc.basesteam.util.FurnaceFuelHelper.getBurnTime(item, getLevel()) > 0; } @Override diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/ElectricBoilerBlock.java b/src/main/java/zone/moddev/mc/basesteam/machines/ElectricBoilerBlock.java index 82b718f..bce1e8b 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/ElectricBoilerBlock.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/ElectricBoilerBlock.java @@ -16,7 +16,7 @@ public class ElectricBoilerBlock extends cyano.poweradvantage.api.simple.BlockS public ElectricBoilerBlock() { - super(machineProperties(0.75F), Power.steam_power, ElectricBoilerTileEntity.ELECTRIC_POWER, Fluids.fluidConduit_general); + super(machineProperties(0.75F).setId(zone.moddev.mc.basesteam.blocks.SteamBlockProperties.key("steam_boiler_electric")), Power.steam_power, ElectricBoilerTileEntity.ELECTRIC_POWER, Fluids.fluidConduit_general); } @Override diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/ElectricBoilerTileEntity.java b/src/main/java/zone/moddev/mc/basesteam/machines/ElectricBoilerTileEntity.java index 882dfa1..14b4acb 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/ElectricBoilerTileEntity.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/ElectricBoilerTileEntity.java @@ -7,7 +7,8 @@ import cyano.poweradvantage.api.fluid.FluidRequest; import zone.moddev.mc.basepower.init.Fluids; import zone.moddev.mc.basesteam.init.Power; -import net.minecraft.core.HolderLookup; +import net.minecraft.world.level.storage.ValueInput; +import net.minecraft.world.level.storage.ValueOutput; import net.minecraft.world.entity.player.Player; import net.minecraft.sounds.SoundEvents; import net.minecraft.world.item.ItemStack; @@ -50,7 +51,7 @@ public ElectricBoilerTileEntity(net.minecraft.core.BlockPos pos, net.minecraft.w @Override protected void onContentsChanged() { if (ElectricBoilerTileEntity.this.getLevel() != null - && !ElectricBoilerTileEntity.this.getLevel().isClientSide) { + && !ElectricBoilerTileEntity.this.getLevel().isClientSide()) { ElectricBoilerTileEntity.this.setChanged(); } } @@ -188,11 +189,11 @@ public void onDataFieldUpdate() { * @param tagRoot An NBT tag */ @Override - protected void saveAdditional(final CompoundTag tagRoot, HolderLookup.Provider registries) { - super.saveAdditional(tagRoot, registries); + protected void saveAdditional(ValueOutput tagRoot) { + super.saveAdditional(tagRoot); CompoundTag tankTag = new CompoundTag(); this.getTank().writeToNBT(tankTag); - tagRoot.put("Tank", tankTag); + tagRoot.store("Tank", CompoundTag.CODEC, tankTag); if(getEnergy(ELECTRIC_POWER) > 0)tagRoot.putFloat("Electricity", getEnergy(ELECTRIC_POWER)); } /** @@ -200,18 +201,18 @@ protected void saveAdditional(final CompoundTag tagRoot, HolderLookup.Provider r * @param tagRoot An NBT tag */ @Override - protected void loadAdditional(final CompoundTag tagRoot, HolderLookup.Provider registries) { - super.loadAdditional(tagRoot, registries); - if (tagRoot.contains("Tank")) { - CompoundTag tankTag = tagRoot.getCompound("Tank"); + protected void loadAdditional(ValueInput tagRoot) { + super.loadAdditional(tagRoot); + if (tagRoot.read("Tank", CompoundTag.CODEC).isPresent()) { + CompoundTag tankTag = tagRoot.read("Tank", CompoundTag.CODEC).orElseGet(CompoundTag::new); getTank().readFromNBT(tankTag); if(tankTag.contains("Empty")){ // empty the tank if NBT says its empty (not default behavior of Tank.read(...) ) getTank().setFluid(null); } } - if(tagRoot.contains("Electricity")){ - this.setEnergy(tagRoot.getFloat("Electricity"),ELECTRIC_POWER); + if(tagRoot.read("Electricity", com.mojang.serialization.Codec.FLOAT).isPresent()){ + this.setEnergy(tagRoot.getFloatOr("Electricity", 0.0F),ELECTRIC_POWER); } else { this.setEnergy(0,ELECTRIC_POWER); } diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/GeothermalBoilerBlock.java b/src/main/java/zone/moddev/mc/basesteam/machines/GeothermalBoilerBlock.java index 0a62b57..e6ac6e1 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/GeothermalBoilerBlock.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/GeothermalBoilerBlock.java @@ -13,7 +13,7 @@ public class GeothermalBoilerBlock extends cyano.poweradvantage.api.simple.BlockSimplePowerMachine{ public GeothermalBoilerBlock() { - super(machineProperties(0.75F), Power.steam_power, Fluids.fluidConduit_general); + super(machineProperties(0.75F).setId(zone.moddev.mc.basesteam.blocks.SteamBlockProperties.key("steam_boiler_geothermal")), Power.steam_power, Fluids.fluidConduit_general); } @Override diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/GeothermalBoilerTileEntity.java b/src/main/java/zone/moddev/mc/basesteam/machines/GeothermalBoilerTileEntity.java index 001480b..ea9aa58 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/GeothermalBoilerTileEntity.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/GeothermalBoilerTileEntity.java @@ -7,7 +7,8 @@ import cyano.poweradvantage.api.fluid.FluidRequest; import zone.moddev.mc.basepower.init.Fluids; import zone.moddev.mc.basesteam.init.Power; -import net.minecraft.core.HolderLookup; +import net.minecraft.world.level.storage.ValueInput; +import net.minecraft.world.level.storage.ValueOutput; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.entity.player.Player; @@ -53,7 +54,7 @@ public GeothermalBoilerTileEntity(net.minecraft.core.BlockPos pos, net.minecraft @Override protected void onContentsChanged() { if (GeothermalBoilerTileEntity.this.getLevel() != null - && !GeothermalBoilerTileEntity.this.getLevel().isClientSide) { + && !GeothermalBoilerTileEntity.this.getLevel().isClientSide()) { GeothermalBoilerTileEntity.this.setChanged(); } } @@ -221,11 +222,11 @@ public void onDataFieldUpdate() { * @param tagRoot An NBT tag */ @Override - protected void saveAdditional(final CompoundTag tagRoot, HolderLookup.Provider registries) { - super.saveAdditional(tagRoot, registries); + protected void saveAdditional(ValueOutput tagRoot) { + super.saveAdditional(tagRoot); CompoundTag tankTag = new CompoundTag(); this.getTank().writeToNBT(tankTag); - tagRoot.put("Tank", tankTag); + tagRoot.store("Tank", CompoundTag.CODEC, tankTag); if(this.temperature > 0)tagRoot.putFloat("Temperature", this.temperature); } /** @@ -233,20 +234,20 @@ protected void saveAdditional(final CompoundTag tagRoot, HolderLookup.Provider r * @param tagRoot An NBT tag */ @Override - protected void loadAdditional(final CompoundTag tagRoot, HolderLookup.Provider registries) { - super.loadAdditional(tagRoot, registries); - if (tagRoot.contains("Tank")) { - CompoundTag tankTag = tagRoot.getCompound("Tank"); + protected void loadAdditional(ValueInput tagRoot) { + super.loadAdditional(tagRoot); + if (tagRoot.read("Tank", CompoundTag.CODEC).isPresent()) { + CompoundTag tankTag = tagRoot.read("Tank", CompoundTag.CODEC).orElseGet(CompoundTag::new); getTank().readFromNBT(tankTag); if(tankTag.contains("Empty")){ // empty the tank if NBT says its empty (not default behavior of Tank.read(...) ) getTank().setFluid(null); } } - if(tagRoot.contains("Temperature")){ - this.temperature = tagRoot.getFloat("Temperature"); - } else if(tagRoot.contains("Tempoerature")){ - this.temperature = tagRoot.getFloat("Tempoerature"); + if(tagRoot.read("Temperature", com.mojang.serialization.Codec.FLOAT).isPresent()){ + this.temperature = tagRoot.getFloatOr("Temperature", 0.0F); + } else if(tagRoot.read("Tempoerature", com.mojang.serialization.Codec.FLOAT).isPresent()){ + this.temperature = tagRoot.getFloatOr("Tempoerature", 0.0F); } else { this.temperature = 0; } diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/OilBoilerBlock.java b/src/main/java/zone/moddev/mc/basesteam/machines/OilBoilerBlock.java index 64541b1..8420a26 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/OilBoilerBlock.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/OilBoilerBlock.java @@ -21,7 +21,7 @@ public class OilBoilerBlock extends cyano.poweradvantage.api.simple.BlockSimpleP public OilBoilerBlock() { - super(machineProperties(0.75F), Power.steam_power, Fluids.fluidConduit_general); + super(machineProperties(0.75F).setId(zone.moddev.mc.basesteam.blocks.SteamBlockProperties.key("steam_boiler_oil")), Power.steam_power, Fluids.fluidConduit_general); } @Override diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/OilBoilerTileEntity.java b/src/main/java/zone/moddev/mc/basesteam/machines/OilBoilerTileEntity.java index 8342b17..3c3d624 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/OilBoilerTileEntity.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/OilBoilerTileEntity.java @@ -12,14 +12,15 @@ import zone.moddev.mc.basepower.registry.FuelRegistry; import zone.moddev.mc.basesteam.SteamAdvantage; import zone.moddev.mc.basesteam.init.Power; -import net.minecraft.core.HolderLookup; +import net.minecraft.world.level.storage.ValueInput; +import net.minecraft.world.level.storage.ValueOutput; import net.minecraft.world.entity.player.Player; import net.minecraft.sounds.SoundEvents; import net.minecraft.world.item.ItemStack; import net.minecraft.nbt.CompoundTag; import net.minecraft.core.Direction; import net.minecraft.sounds.SoundSource; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.level.material.Fluid; import cyano.poweradvantage.api.fluid.FluidStack; import cyano.poweradvantage.api.fluid.FluidTank; @@ -148,7 +149,7 @@ private static Float getBurnTimePerBucketFor(Fluid fluid){ } private static Float getConfiguredBurnTimePerBucketFor(Fluid fluid) { - ResourceLocation id = net.minecraftforge.registries.ForgeRegistries.FLUIDS.getKey(fluid); + Identifier id = net.minecraftforge.registries.ForgeRegistries.FLUIDS.getKey(fluid); if (id != null) { if (SteamAdvantage.fluidBurnValues.containsKey(id.toString())) return SteamAdvantage.fluidBurnValues.get(id.toString()); if (SteamAdvantage.fluidBurnValues.containsKey(id.getPath())) return SteamAdvantage.fluidBurnValues.get(id.getPath()); @@ -264,14 +265,14 @@ public void onDataFieldUpdate() { * @param tagRoot An NBT tag */ @Override - protected void saveAdditional(final CompoundTag tagRoot, HolderLookup.Provider registries) { - super.saveAdditional(tagRoot, registries); + protected void saveAdditional(ValueOutput tagRoot) { + super.saveAdditional(tagRoot); CompoundTag tankTag = new CompoundTag(); this.getWaterTank().writeToNBT(tankTag); - tagRoot.put("WaterTank", tankTag); + tagRoot.store("WaterTank", CompoundTag.CODEC, tankTag); CompoundTag tankTag2 = new CompoundTag(); this.getFuelTank().writeToNBT(tankTag2); - tagRoot.put("FuelTank", tankTag2); + tagRoot.store("FuelTank", CompoundTag.CODEC, tankTag2); if(this.burnTime > 0)tagRoot.putInt("BurnTime", this.burnTime); if(this.totalBurnTime > 0)tagRoot.putInt("BurnTimeTotal", this.totalBurnTime); } @@ -280,31 +281,31 @@ protected void saveAdditional(final CompoundTag tagRoot, HolderLookup.Provider r * @param tagRoot An NBT tag */ @Override - protected void loadAdditional(final CompoundTag tagRoot, HolderLookup.Provider registries) { - super.loadAdditional(tagRoot, registries); - if (tagRoot.contains("WaterTank")) { - CompoundTag tankTag = tagRoot.getCompound("WaterTank"); + protected void loadAdditional(ValueInput tagRoot) { + super.loadAdditional(tagRoot); + if (tagRoot.read("WaterTank", CompoundTag.CODEC).isPresent()) { + CompoundTag tankTag = tagRoot.read("WaterTank", CompoundTag.CODEC).orElseGet(CompoundTag::new); getWaterTank().readFromNBT(tankTag); if(tankTag.contains("Empty")){ // empty the tank if NBT says its empty (not default behavior of Tank.read(...) ) getWaterTank().setFluid(null); } } - if (tagRoot.contains("FuelTank")) { - CompoundTag tankTag = tagRoot.getCompound("FuelTank"); + if (tagRoot.read("FuelTank", CompoundTag.CODEC).isPresent()) { + CompoundTag tankTag = tagRoot.read("FuelTank", CompoundTag.CODEC).orElseGet(CompoundTag::new); getFuelTank().readFromNBT(tankTag); if(tankTag.contains("Empty")){ // empty the tank if NBT says its empty (not default behavior of Tank.read(...) ) getFuelTank().setFluid(null); } } - if(tagRoot.contains("BurnTime")){ - this.burnTime = tagRoot.getInt("BurnTime"); + if(tagRoot.read("BurnTime", com.mojang.serialization.Codec.INT).isPresent()){ + this.burnTime = tagRoot.getIntOr("BurnTime", 0); } else { this.burnTime = 0; } - if(tagRoot.contains("BurnTimeTotal")){ - this.totalBurnTime = tagRoot.getInt("BurnTimeTotal"); + if(tagRoot.read("BurnTimeTotal", com.mojang.serialization.Codec.INT).isPresent()){ + this.totalBurnTime = tagRoot.getIntOr("BurnTimeTotal", 0); } else { this.totalBurnTime = 0; } diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/RockCrusherBlock.java b/src/main/java/zone/moddev/mc/basesteam/machines/RockCrusherBlock.java index 9ee77a5..7e1b7f0 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/RockCrusherBlock.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/RockCrusherBlock.java @@ -16,7 +16,7 @@ public class RockCrusherBlock extends cyano.poweradvantage.api.simple.BlockSimpl public RockCrusherBlock() { - super(machineProperties(0.75F), Power.steam_power); + super(machineProperties(0.75F).setId(zone.moddev.mc.basesteam.blocks.SteamBlockProperties.key("steam_crusher")), Power.steam_power); } @Override diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/RockCrusherTileEntity.java b/src/main/java/zone/moddev/mc/basesteam/machines/RockCrusherTileEntity.java index 5145c2d..59cc916 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/RockCrusherTileEntity.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/RockCrusherTileEntity.java @@ -1,7 +1,8 @@ package zone.moddev.mc.basesteam.machines; import cyano.poweradvantage.api.ConduitType; -import net.minecraft.core.HolderLookup; +import net.minecraft.world.level.storage.ValueInput; +import net.minecraft.world.level.storage.ValueOutput; import net.minecraft.world.entity.player.Player; import net.minecraft.sounds.SoundEvents; import net.minecraft.world.item.ItemStack; @@ -200,16 +201,16 @@ private int canAddToOutputInventory(ItemStack output) { } @Override - protected void saveAdditional(CompoundTag tagRoot, HolderLookup.Provider registries){ - super.saveAdditional(tagRoot, registries); + protected void saveAdditional(ValueOutput tagRoot){ + super.saveAdditional(tagRoot); tagRoot.putShort("progress",(short)progress); } @Override - protected void loadAdditional(CompoundTag tagRoot, HolderLookup.Provider registries){ - super.loadAdditional(tagRoot, registries); - if(tagRoot.contains("progress")){ - progress = tagRoot.getShort("progress"); + protected void loadAdditional(ValueInput tagRoot){ + super.loadAdditional(tagRoot); + if(tagRoot.read("progress", com.mojang.serialization.Codec.SHORT).isPresent()){ + progress = tagRoot.getShortOr("progress", (short)0); } } diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/SteamDrillBlock.java b/src/main/java/zone/moddev/mc/basesteam/machines/SteamDrillBlock.java index 8e32125..9b49a38 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/SteamDrillBlock.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/SteamDrillBlock.java @@ -5,7 +5,7 @@ import zone.moddev.mc.basepower.conduitnetwork.ConduitRegistry; import zone.moddev.mc.basesteam.blocks.DrillBitTileEntity; import zone.moddev.mc.basesteam.init.Power; -import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.level.block.state.properties.EnumProperty; import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.BlockState; @@ -28,10 +28,10 @@ public class SteamDrillBlock extends GUIBlock implements ITypedConduit { /** * Blockstate property */ - public static final DirectionProperty FACING = BlockStateProperties.FACING; + public static final EnumProperty FACING = BlockStateProperties.FACING; public SteamDrillBlock(){ - super(machineProperties(0.75F)); + super(machineProperties(0.75F).setId(zone.moddev.mc.basesteam.blocks.SteamBlockProperties.key("steam_drill"))); this.registerDefaultState(getStateDefinition().any().setValue(FACING, Direction.DOWN)); } @@ -47,18 +47,16 @@ public void onPlace(final BlockState state, final Level world, final BlockPos co * This method is called when the block is removed from the world by an entity. */ @Override - public void onRemove(BlockState state, Level w, BlockPos coord, BlockState newState, boolean isMoving){ - if (state.getBlock() != newState.getBlock()) { - destroyNeighbors(w, coord, state); - ConduitRegistry.getInstance().conduitBlockRemovedEvent(w, coord, Power.steam_power); - } - super.onRemove(state, w, coord, newState, isMoving); + public void affectNeighborsAfterRemoval(BlockState state, net.minecraft.server.level.ServerLevel w, + BlockPos coord, boolean isMoving){ + ConduitRegistry.getInstance().conduitBlockRemovedEvent(w, coord, Power.steam_power); + super.affectNeighborsAfterRemoval(state, w, coord, isMoving); } /** * This method is called when the block is destroyed by an explosion. */ @Override - public void wasExploded(Level w, BlockPos coord, Explosion boom){ + public void wasExploded(net.minecraft.server.level.ServerLevel w, BlockPos coord, Explosion boom){ destroyNeighbors(w,coord,w.getBlockState(coord)); ConduitRegistry.getInstance().conduitBlockRemovedEvent(w, coord, Power.steam_power); super.wasExploded(w, coord, boom); @@ -66,8 +64,8 @@ public void wasExploded(Level w, BlockPos coord, Explosion boom){ - private void destroyNeighbors(Level w, BlockPos coord, BlockState state) { - if(w.isClientSide) return; + static void destroyNeighbors(Level w, BlockPos coord, BlockState state) { + if(w.isClientSide()) return; // destroy connected drill bits for(int i = 0; i < 6; i++){ BlockPos pos = coord.relative(Direction.from3DDataValue(i)); diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/SteamDrillTileEntity.java b/src/main/java/zone/moddev/mc/basesteam/machines/SteamDrillTileEntity.java index 9c52cb9..ddb3287 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/SteamDrillTileEntity.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/SteamDrillTileEntity.java @@ -6,7 +6,8 @@ import zone.moddev.mc.basesteam.compat.BaseMetalsCompat; import zone.moddev.mc.basesteam.init.Blocks; import zone.moddev.mc.basesteam.init.Power; -import net.minecraft.core.HolderLookup; +import net.minecraft.world.level.storage.ValueInput; +import net.minecraft.world.level.storage.ValueOutput; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.entity.player.Player; @@ -193,7 +194,8 @@ private boolean followTrack(){ dataTransfer.putInt("x", nextPos.getX()); dataTransfer.putInt("y", nextPos.getY()); dataTransfer.putInt("z", nextPos.getZ()); - te.loadWithComponents(dataTransfer, w.registryAccess()); + te.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, w.registryAccess(), dataTransfer)); te.clearRemoved(); te.subtractEnergy(ENERGY_COST_MOVE, Power.steam_power); te.setChanged(); @@ -346,7 +348,7 @@ private void targetBlock (BlockPos n){ } private void restoreTargetBlock(){ - if(powerAdvantageWorld() == null || powerAdvantageWorld().isClientSide + if(powerAdvantageWorld() == null || powerAdvantageWorld().isClientSide() || !powerAdvantageWorld().hasChunkAt(targetBlockCoord)) return; int savedProgress = progress; if(savedTargetState != null && !savedTargetState.equals( @@ -468,8 +470,8 @@ private void inventoryTransfer(BlockPos adj, Direction otherFace) { } @Override - protected void saveAdditional(CompoundTag tagRoot, HolderLookup.Provider registries){ - super.saveAdditional(tagRoot, registries); + protected void saveAdditional(ValueOutput tagRoot){ + super.saveAdditional(tagRoot); tagRoot.putShort("progress",(short)progress); // getFacing refreshes this during normal ticks. World lookups here can // synchronously reload a chunk while its unload-save is in progress. @@ -478,30 +480,36 @@ protected void saveAdditional(CompoundTag tagRoot, HolderLookup.Provider registr tagRoot.putInt("targetX", targetBlockCoord.getX()); tagRoot.putInt("targetY", targetBlockCoord.getY()); tagRoot.putInt("targetZ", targetBlockCoord.getZ()); - if(savedTargetState != null) tagRoot.put("TargetState", savedTargetState.copy()); + if(savedTargetState != null) tagRoot.store("TargetState", CompoundTag.CODEC, savedTargetState.copy()); } } + + @Override + public void preRemoveSideEffects(BlockPos pos, BlockState state) { + if (getLevel() != null) SteamDrillBlock.destroyNeighbors(getLevel(), pos, state); + super.preRemoveSideEffects(pos, state); + } @Override - protected void loadAdditional(CompoundTag tagRoot, HolderLookup.Provider registries){ - super.loadAdditional(tagRoot, registries); + protected void loadAdditional(ValueInput tagRoot){ + super.loadAdditional(tagRoot); untargetBlock(); savedFacing = null; oldDir = null; - if(tagRoot.contains("Facing")){ - int facingIndex = tagRoot.getByte("Facing"); + if(tagRoot.read("Facing", com.mojang.serialization.Codec.BYTE).isPresent()){ + int facingIndex = tagRoot.getByteOr("Facing", (byte)0); if(facingIndex >= 0 && facingIndex < Direction.values().length){ savedFacing = Direction.from3DDataValue(facingIndex); } } - if(tagRoot.contains("progress")){ - progress = tagRoot.getShort("progress"); + if(tagRoot.read("progress", com.mojang.serialization.Codec.SHORT).isPresent()){ + progress = tagRoot.getShortOr("progress", (short)0); } - if(tagRoot.contains("targetX") && tagRoot.contains("targetY") && tagRoot.contains("targetZ")){ - int x = tagRoot.getInt("targetX"); - int y = tagRoot.getInt("targetY"); - int z = tagRoot.getInt("targetZ"); - savedTargetState = tagRoot.contains("TargetState", 10) ? tagRoot.getCompound("TargetState").copy() : null; + if(tagRoot.read("targetX", com.mojang.serialization.Codec.INT).isPresent() && tagRoot.read("targetY", com.mojang.serialization.Codec.INT).isPresent() && tagRoot.read("targetZ", com.mojang.serialization.Codec.INT).isPresent()){ + int x = tagRoot.getIntOr("targetX", 0); + int y = tagRoot.getIntOr("targetY", 0); + int z = tagRoot.getIntOr("targetZ", 0); + savedTargetState = tagRoot.read("TargetState", CompoundTag.CODEC).isPresent() ? tagRoot.read("TargetState", CompoundTag.CODEC).orElseGet(CompoundTag::new).copy() : null; // Loot and hardness are restored on the server after the world is attached. this.deferredTargetBlock(new BlockPos(x,y,z)); } diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/SteamElevatorBlock.java b/src/main/java/zone/moddev/mc/basesteam/machines/SteamElevatorBlock.java index d061eda..8a7bf75 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/SteamElevatorBlock.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/SteamElevatorBlock.java @@ -16,7 +16,7 @@ public class SteamElevatorBlock extends cyano.poweradvantage.api.simple.BlockSim public SteamElevatorBlock() { - super(machineProperties(0.75F), Power.steam_power); + super(machineProperties(0.75F).setId(zone.moddev.mc.basesteam.blocks.SteamBlockProperties.key("steam_elevator")), Power.steam_power); } @Override diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/SteamElevatorTileEntity.java b/src/main/java/zone/moddev/mc/basesteam/machines/SteamElevatorTileEntity.java index 051e3f4..5b89225 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/SteamElevatorTileEntity.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/SteamElevatorTileEntity.java @@ -3,7 +3,8 @@ import cyano.poweradvantage.api.ConduitType; import zone.moddev.mc.basesteam.init.Blocks; import zone.moddev.mc.basesteam.init.Power; -import net.minecraft.core.HolderLookup; +import net.minecraft.world.level.storage.ValueInput; +import net.minecraft.world.level.storage.ValueOutput; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraft.sounds.SoundEvents; @@ -173,16 +174,16 @@ public void onDataFieldUpdate() { @Override - protected void saveAdditional(CompoundTag tagRoot, HolderLookup.Provider registries){ - super.saveAdditional(tagRoot, registries); + protected void saveAdditional(ValueOutput tagRoot){ + super.saveAdditional(tagRoot); tagRoot.putBoolean("up",above); } @Override - protected void loadAdditional(CompoundTag tagRoot, HolderLookup.Provider registries){ - super.loadAdditional(tagRoot, registries); - if(tagRoot.contains("up")){ - above = tagRoot.getBoolean("up"); + protected void loadAdditional(ValueInput tagRoot){ + super.loadAdditional(tagRoot); + if(tagRoot.read("up", com.mojang.serialization.Codec.BOOL).isPresent()){ + above = tagRoot.getBooleanOr("up", false); } } diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/SteamPumpBlock.java b/src/main/java/zone/moddev/mc/basesteam/machines/SteamPumpBlock.java index ea97183..88831a2 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/SteamPumpBlock.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/SteamPumpBlock.java @@ -22,7 +22,7 @@ public class SteamPumpBlock extends cyano.poweradvantage.api.simple.BlockSimpleP public SteamPumpBlock() { - super(machineProperties(0.75F), Power.steam_power, Fluids.fluidConduit_general); + super(machineProperties(0.75F).setId(zone.moddev.mc.basesteam.blocks.SteamBlockProperties.key("steam_pump")), Power.steam_power, Fluids.fluidConduit_general); } @Override @@ -56,20 +56,14 @@ public boolean isPowerSource(ConduitType type){ return !ConduitType.areSameType(Power.steam_power, type); } @Override - public void onRemove(BlockState state, Level w, BlockPos coord, BlockState newState, boolean isMoving) { - if (state.getBlock() != newState.getBlock()) destroyPipe(w, coord); - super.onRemove(state, w, coord, newState, isMoving); - } - - @Override - public void wasExploded(Level w, BlockPos coord, Explosion boom){ + public void wasExploded(net.minecraft.server.level.ServerLevel w, BlockPos coord, Explosion boom){ destroyPipe(w, coord); super.wasExploded(w, coord, boom); } - private void destroyPipe(Level w, BlockPos coord) { - if(w.isClientSide) return; + static void destroyPipe(Level w, BlockPos coord) { + if(w.isClientSide()) return; // destroy connected drill bits BlockPos c = coord.below(); while(c.getY() > 0 && w.getBlockState(c).getBlock() == Blocks.pump_pipe_steam){ diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/SteamPumpTileEntity.java b/src/main/java/zone/moddev/mc/basesteam/machines/SteamPumpTileEntity.java index 83427d9..0f9b516 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/SteamPumpTileEntity.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/SteamPumpTileEntity.java @@ -10,7 +10,8 @@ import zone.moddev.mc.basepower.init.Fluids; import zone.moddev.mc.basepower.util.FluidHandlerHelper; import zone.moddev.mc.basesteam.init.Power; -import net.minecraft.core.HolderLookup; +import net.minecraft.world.level.storage.ValueInput; +import net.minecraft.world.level.storage.ValueOutput; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.BucketPickup; import net.minecraft.world.entity.player.Player; @@ -285,23 +286,29 @@ public void onDataFieldUpdate() { * @param tagRoot An NBT tag */ @Override - protected void saveAdditional(final CompoundTag tagRoot, HolderLookup.Provider registries) { - super.saveAdditional(tagRoot, registries); + protected void saveAdditional(ValueOutput tagRoot) { + super.saveAdditional(tagRoot); CompoundTag tankTag = new CompoundTag(); this.getTank().writeToNBT(tankTag); - tagRoot.put("Tank", tankTag); + tagRoot.store("Tank", CompoundTag.CODEC, tankTag); tagRoot.putByte("NextPump", (byte)this.timeUntilNextPump); } + + @Override + public void preRemoveSideEffects(BlockPos pos, BlockState state) { + if (getLevel() != null) SteamPumpBlock.destroyPipe(getLevel(), pos); + super.preRemoveSideEffects(pos, state); + } /** * Handles data saving and loading * @param tagRoot An NBT tag */ @Override - protected void loadAdditional(final CompoundTag tagRoot, HolderLookup.Provider registries) { - super.loadAdditional(tagRoot, registries); - String tankKey = tagRoot.contains("Tank") ? "Tank" : "TankOut"; - if (tagRoot.contains(tankKey)) { - CompoundTag tankTag = tagRoot.getCompound(tankKey); + protected void loadAdditional(ValueInput tagRoot) { + super.loadAdditional(tagRoot); + String tankKey = tagRoot.read("Tank", CompoundTag.CODEC).isPresent() ? "Tank" : "TankOut"; + if (tagRoot.read(tankKey, CompoundTag.CODEC).isPresent()) { + CompoundTag tankTag = tagRoot.read(tankKey, CompoundTag.CODEC).orElseGet(CompoundTag::new); getTank().readFromNBT(tankTag); if(tankTag.contains("Empty")){ // empty the tank if NBT says its empty (not default behavior of Tank.read(...) ) @@ -309,8 +316,8 @@ protected void loadAdditional(final CompoundTag tagRoot, HolderLookup.Provider r } } - if (tagRoot.contains("NextPump")) { - this.timeUntilNextPump = tagRoot.getByte("NextPump"); + if (tagRoot.read("NextPump", com.mojang.serialization.Codec.BYTE).isPresent()) { + this.timeUntilNextPump = tagRoot.getByteOr("NextPump", (byte)0); } } diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/SteamStillBlock.java b/src/main/java/zone/moddev/mc/basesteam/machines/SteamStillBlock.java index e8fc782..283395b 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/SteamStillBlock.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/SteamStillBlock.java @@ -21,7 +21,7 @@ public class SteamStillBlock extends cyano.poweradvantage.api.simple.BlockSimple public SteamStillBlock() { - super(machineProperties(0.75F), Power.steam_power, Fluids.fluidConduit_general); + super(machineProperties(0.75F).setId(zone.moddev.mc.basesteam.blocks.SteamBlockProperties.key("steam_still")), Power.steam_power, Fluids.fluidConduit_general); } @Override diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/SteamStillTileEntity.java b/src/main/java/zone/moddev/mc/basesteam/machines/SteamStillTileEntity.java index 97907a5..f3e721b 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/SteamStillTileEntity.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/SteamStillTileEntity.java @@ -12,7 +12,8 @@ import zone.moddev.mc.basepower.registry.still.recipe.DistillationRecipe; import zone.moddev.mc.basepower.registry.still.recipe.DistillationRecipeRegistry; import zone.moddev.mc.basesteam.init.Power; -import net.minecraft.core.HolderLookup; +import net.minecraft.world.level.storage.ValueInput; +import net.minecraft.world.level.storage.ValueOutput; import net.minecraft.world.entity.player.Player; import net.minecraft.sounds.SoundEvents; import net.minecraft.world.item.ItemStack; @@ -177,24 +178,24 @@ public void onDataFieldUpdate() { * @param tagRoot An NBT tag */ @Override - protected void saveAdditional(final CompoundTag tagRoot, HolderLookup.Provider registries) { - super.saveAdditional(tagRoot, registries); + protected void saveAdditional(ValueOutput tagRoot) { + super.saveAdditional(tagRoot); CompoundTag tankTag = new CompoundTag(); this.getOutputTank().writeToNBT(tankTag); - tagRoot.put("TankOut", tankTag); + tagRoot.store("TankOut", CompoundTag.CODEC, tankTag); CompoundTag tankTag2 = new CompoundTag(); this.getInputTank().writeToNBT(tankTag2); - tagRoot.put("TankIn", tankTag2); + tagRoot.store("TankIn", CompoundTag.CODEC, tankTag2); } /** * Handles data saving and loading * @param tagRoot An NBT tag */ @Override - protected void loadAdditional(final CompoundTag tagRoot, HolderLookup.Provider registries) { - super.loadAdditional(tagRoot, registries); - if (tagRoot.contains("TankOut")) { - CompoundTag tankTag = tagRoot.getCompound("TankOut"); + protected void loadAdditional(ValueInput tagRoot) { + super.loadAdditional(tagRoot); + if (tagRoot.read("TankOut", CompoundTag.CODEC).isPresent()) { + CompoundTag tankTag = tagRoot.read("TankOut", CompoundTag.CODEC).orElseGet(CompoundTag::new); getOutputTank().readFromNBT(tankTag); if(tankTag.contains("Empty")){ // empty the tank if NBT says its empty (not default behavior of Tank.read(...) ) @@ -202,8 +203,8 @@ protected void loadAdditional(final CompoundTag tagRoot, HolderLookup.Provider r } } - if (tagRoot.contains("TankIn")) { - CompoundTag tankTag = tagRoot.getCompound("TankIn"); + if (tagRoot.read("TankIn", CompoundTag.CODEC).isPresent()) { + CompoundTag tankTag = tagRoot.read("TankIn", CompoundTag.CODEC).orElseGet(CompoundTag::new); getInputTank().readFromNBT(tankTag); if(tankTag.contains("Empty")){ // empty the tank if NBT says its empty (not default behavior of Tank.read(...) ) diff --git a/src/main/java/zone/moddev/mc/basesteam/machines/SteamTankBlock.java b/src/main/java/zone/moddev/mc/basesteam/machines/SteamTankBlock.java index cd65238..e0ccd14 100644 --- a/src/main/java/zone/moddev/mc/basesteam/machines/SteamTankBlock.java +++ b/src/main/java/zone/moddev/mc/basesteam/machines/SteamTankBlock.java @@ -11,7 +11,7 @@ public class SteamTankBlock extends cyano.poweradvantage.api.simple.BlockSimplePowerMachine { public SteamTankBlock(){ - super(machineProperties(0.75F), Power.steam_power); + super(machineProperties(0.75F).setId(zone.moddev.mc.basesteam.blocks.SteamBlockProperties.key("steam_tank")), Power.steam_power); } @Override diff --git a/src/main/java/zone/moddev/mc/basesteam/migration/SteamLegacyMigrations.java b/src/main/java/zone/moddev/mc/basesteam/migration/SteamLegacyMigrations.java index 88b7d80..4bbbdcb 100644 --- a/src/main/java/zone/moddev/mc/basesteam/migration/SteamLegacyMigrations.java +++ b/src/main/java/zone/moddev/mc/basesteam/migration/SteamLegacyMigrations.java @@ -6,7 +6,7 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.nbt.CompoundTag; import net.minecraft.core.Direction; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import zone.moddev.mc.basepower.blocks.BlockPowerSwitch; import zone.moddev.mc.basesteam.SteamAdvantage; import zone.moddev.mc.basesteam.blocks.PlatformBlock; @@ -80,15 +80,15 @@ static int legacyPlatformHeight(int metadata) { } static boolean repairPersistenceKeys(CompoundTag compound) { - String id = compound.getString("id"); + String id = compound.getStringOr("id", ""); boolean changed = false; - if (isTile(id, "steam_pump") && !compound.contains("Tank", 10) && compound.contains("TankOut", 10)) { - compound.put("Tank", compound.getCompound("TankOut").copy()); + if (isTile(id, "steam_pump") && compound.getCompound("Tank").isEmpty() && compound.getCompound("TankOut").isPresent()) { + compound.put("Tank", compound.getCompoundOrEmpty("TankOut").copy()); changed = true; } if (isTile(id, "steam_boiler_geothermal") - && !compound.contains("Temperature", 99) && compound.contains("Tempoerature", 99)) { - compound.putFloat("Temperature", compound.getFloat("Tempoerature")); + && compound.getFloat("Temperature").isEmpty() && compound.getFloat("Tempoerature").isPresent()) { + compound.putFloat("Temperature", compound.getFloatOr("Tempoerature", 0.0F)); changed = true; } return changed; @@ -104,7 +104,7 @@ private static boolean matchesTileId(String value, String namespace, String path || dotted.equalsIgnoreCase(value) || ("minecraft:" + dotted).equalsIgnoreCase(value); } - private static ResourceLocation id(String path) { - return ResourceLocation.fromNamespaceAndPath(SteamAdvantage.MODID, path); + private static Identifier id(String path) { + return Identifier.fromNamespaceAndPath(SteamAdvantage.MODID, path); } } diff --git a/src/main/java/zone/moddev/mc/basesteam/recipe/RecipeConditions.java b/src/main/java/zone/moddev/mc/basesteam/recipe/RecipeConditions.java index a4ef06c..61a708f 100644 --- a/src/main/java/zone/moddev/mc/basesteam/recipe/RecipeConditions.java +++ b/src/main/java/zone/moddev/mc/basesteam/recipe/RecipeConditions.java @@ -5,7 +5,7 @@ import com.mojang.serialization.DynamicOps; import com.mojang.serialization.MapCodec; import net.minecraftforge.common.crafting.conditions.ICondition; -import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.eventbus.api.bus.BusGroup; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.ForgeRegistries; import zone.moddev.mc.basepower.PowerAdvantage; @@ -25,7 +25,7 @@ public final class RecipeConditions { private RecipeConditions() {} - public static void register(IEventBus bus) { + public static void register(BusGroup bus) { SERIALIZERS.register(bus); } diff --git a/src/main/java/zone/moddev/mc/basesteam/util/FurnaceFuelHelper.java b/src/main/java/zone/moddev/mc/basesteam/util/FurnaceFuelHelper.java index bdbb53a..a56e245 100644 --- a/src/main/java/zone/moddev/mc/basesteam/util/FurnaceFuelHelper.java +++ b/src/main/java/zone/moddev/mc/basesteam/util/FurnaceFuelHelper.java @@ -1,13 +1,23 @@ package zone.moddev.mc.basesteam.util; import net.minecraft.world.item.ItemStack; -import net.minecraftforge.common.ForgeHooks; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.FuelValues; /** Forge-aware access to the vanilla furnace fuel calculation. */ public final class FurnaceFuelHelper { private FurnaceFuelHelper() {} public static int getBurnTime(ItemStack stack) { - return ForgeHooks.getBurnTime(stack, null); + var server = net.minecraftforge.server.ServerLifecycleHooks.getCurrentServer(); + FuelValues fuels = server == null ? FuelValues.vanillaBurnTimes( + net.minecraft.core.RegistryAccess.fromRegistryOfRegistries( + net.minecraft.core.registries.BuiltInRegistries.REGISTRY), + net.minecraft.world.flag.FeatureFlags.DEFAULT_FLAGS) : server.fuelValues(); + return fuels.burnDuration(stack, null); + } + + public static int getBurnTime(ItemStack stack, Level level) { + return level == null ? getBurnTime(stack) : level.fuelValues().burnDuration(stack, null); } } diff --git a/src/main/java/zone/moddev/mc/basesteam/util/SoundHelper.java b/src/main/java/zone/moddev/mc/basesteam/util/SoundHelper.java index 4161405..d984bb4 100644 --- a/src/main/java/zone/moddev/mc/basesteam/util/SoundHelper.java +++ b/src/main/java/zone/moddev/mc/basesteam/util/SoundHelper.java @@ -23,7 +23,7 @@ public static void playSoundAtTileEntity(SoundEvent sound, SoundSource soundType } public static void playSoundAtPosition(double x, double y, double z, SoundEvent sound, SoundSource soundType, float volume, float pitch, Level serverWorld){ - if(serverWorld.isClientSide) return; + if(serverWorld.isClientSide()) return; final double range = 16; final AABB area = new AABB( x - range, y - range, z - range, @@ -39,7 +39,7 @@ public static void playSoundAtPosition(double x, double y, double z, SoundEvent } public static void playBigSoundAtPosition(double x, double y, double z, SoundEvent sound, SoundSource soundType, float volume, float pitch, Level serverWorld){ - if(serverWorld.isClientSide) return; + if(serverWorld.isClientSide()) return; final double range = 50; final AABB area = new AABB( x - range, y - range, z - range, diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index b6dd11c..6c44f9b 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -1,6 +1,7 @@ modLoader="javafml" loaderVersion="${loader_version_range}" license="GPL-3.0-only" +showAsResourcePack=false issueTrackerURL="https://github.com/MinecraftModDevelopmentMods/baseSteam/issues" [[mods]] @@ -21,7 +22,7 @@ side="BOTH" [[dependencies.basesteam]] modId="basemetals" mandatory=false -versionRange="[3.1.2.121011,4.0.0)" +versionRange="[3.1.2.121111,4.0.0)" ordering="AFTER" side="BOTH" diff --git a/src/main/resources/assets/basesteam/items/blackpowder_cartridge.json b/src/main/resources/assets/basesteam/items/blackpowder_cartridge.json new file mode 100644 index 0000000..79e96a3 --- /dev/null +++ b/src/main/resources/assets/basesteam/items/blackpowder_cartridge.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "basesteam:item/blackpowder_cartridge" + } +} diff --git a/src/main/resources/assets/basesteam/items/drillbit.json b/src/main/resources/assets/basesteam/items/drillbit.json new file mode 100644 index 0000000..484c31a --- /dev/null +++ b/src/main/resources/assets/basesteam/items/drillbit.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "basesteam:item/drillbit" + } +} diff --git a/src/main/resources/assets/basesteam/items/musket.json b/src/main/resources/assets/basesteam/items/musket.json new file mode 100644 index 0000000..98823aa --- /dev/null +++ b/src/main/resources/assets/basesteam/items/musket.json @@ -0,0 +1,24 @@ +{ + "model": { + "type": "minecraft:condition", + "property": "minecraft:component", + "predicate": "minecraft:custom_data", + "value": "{loaded:1b}", + "on_true": { + "type": "minecraft:model", + "model": "basesteam:item/musket_loaded" + }, + "on_false": { + "type": "minecraft:condition", + "property": "minecraft:using_item", + "on_true": { + "type": "minecraft:model", + "model": "basesteam:item/musket_loading" + }, + "on_false": { + "type": "minecraft:model", + "model": "basesteam:item/musket_unloaded" + } + } + } +} diff --git a/src/main/resources/assets/basesteam/items/platform.json b/src/main/resources/assets/basesteam/items/platform.json new file mode 100644 index 0000000..03b5100 --- /dev/null +++ b/src/main/resources/assets/basesteam/items/platform.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "basesteam:item/platform" + } +} diff --git a/src/main/resources/assets/basesteam/items/pump_pipe_steam.json b/src/main/resources/assets/basesteam/items/pump_pipe_steam.json new file mode 100644 index 0000000..0681a95 --- /dev/null +++ b/src/main/resources/assets/basesteam/items/pump_pipe_steam.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "basesteam:item/pump_pipe_steam" + } +} diff --git a/src/main/resources/assets/basesteam/items/steam_boiler_coal.json b/src/main/resources/assets/basesteam/items/steam_boiler_coal.json new file mode 100644 index 0000000..873d815 --- /dev/null +++ b/src/main/resources/assets/basesteam/items/steam_boiler_coal.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "basesteam:item/steam_boiler_coal" + } +} diff --git a/src/main/resources/assets/basesteam/items/steam_boiler_electric.json b/src/main/resources/assets/basesteam/items/steam_boiler_electric.json new file mode 100644 index 0000000..ae65c66 --- /dev/null +++ b/src/main/resources/assets/basesteam/items/steam_boiler_electric.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "basesteam:item/steam_boiler_electric" + } +} diff --git a/src/main/resources/assets/basesteam/items/steam_boiler_geothermal.json b/src/main/resources/assets/basesteam/items/steam_boiler_geothermal.json new file mode 100644 index 0000000..78f544c --- /dev/null +++ b/src/main/resources/assets/basesteam/items/steam_boiler_geothermal.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "basesteam:item/steam_boiler_geothermal" + } +} diff --git a/src/main/resources/assets/basesteam/items/steam_boiler_oil.json b/src/main/resources/assets/basesteam/items/steam_boiler_oil.json new file mode 100644 index 0000000..3498603 --- /dev/null +++ b/src/main/resources/assets/basesteam/items/steam_boiler_oil.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "basesteam:item/steam_boiler_oil" + } +} diff --git a/src/main/resources/assets/basesteam/items/steam_crusher.json b/src/main/resources/assets/basesteam/items/steam_crusher.json new file mode 100644 index 0000000..8d53a44 --- /dev/null +++ b/src/main/resources/assets/basesteam/items/steam_crusher.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "basesteam:item/steam_crusher" + } +} diff --git a/src/main/resources/assets/basesteam/items/steam_drill.json b/src/main/resources/assets/basesteam/items/steam_drill.json new file mode 100644 index 0000000..165b071 --- /dev/null +++ b/src/main/resources/assets/basesteam/items/steam_drill.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "basesteam:item/steam_drill" + } +} diff --git a/src/main/resources/assets/basesteam/items/steam_drill_bit.json b/src/main/resources/assets/basesteam/items/steam_drill_bit.json new file mode 100644 index 0000000..074686c --- /dev/null +++ b/src/main/resources/assets/basesteam/items/steam_drill_bit.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "basesteam:item/steam_drill_bit" + } +} diff --git a/src/main/resources/assets/basesteam/items/steam_elevator.json b/src/main/resources/assets/basesteam/items/steam_elevator.json new file mode 100644 index 0000000..9bb3d0d --- /dev/null +++ b/src/main/resources/assets/basesteam/items/steam_elevator.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "basesteam:item/steam_elevator" + } +} diff --git a/src/main/resources/assets/basesteam/items/steam_furnace.json b/src/main/resources/assets/basesteam/items/steam_furnace.json new file mode 100644 index 0000000..9904525 --- /dev/null +++ b/src/main/resources/assets/basesteam/items/steam_furnace.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "basesteam:item/steam_furnace" + } +} diff --git a/src/main/resources/assets/basesteam/items/steam_governor.json b/src/main/resources/assets/basesteam/items/steam_governor.json new file mode 100644 index 0000000..eaa8848 --- /dev/null +++ b/src/main/resources/assets/basesteam/items/steam_governor.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "basesteam:item/steam_governor" + } +} diff --git a/src/main/resources/assets/basesteam/items/steam_pipe.json b/src/main/resources/assets/basesteam/items/steam_pipe.json new file mode 100644 index 0000000..58a39ec --- /dev/null +++ b/src/main/resources/assets/basesteam/items/steam_pipe.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "basesteam:item/steam_pipe" + } +} diff --git a/src/main/resources/assets/basesteam/items/steam_pump.json b/src/main/resources/assets/basesteam/items/steam_pump.json new file mode 100644 index 0000000..22953e0 --- /dev/null +++ b/src/main/resources/assets/basesteam/items/steam_pump.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "basesteam:item/steam_pump" + } +} diff --git a/src/main/resources/assets/basesteam/items/steam_still.json b/src/main/resources/assets/basesteam/items/steam_still.json new file mode 100644 index 0000000..32b111e --- /dev/null +++ b/src/main/resources/assets/basesteam/items/steam_still.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "basesteam:item/steam_still" + } +} diff --git a/src/main/resources/assets/basesteam/items/steam_switch.json b/src/main/resources/assets/basesteam/items/steam_switch.json new file mode 100644 index 0000000..2d5f8f1 --- /dev/null +++ b/src/main/resources/assets/basesteam/items/steam_switch.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "basesteam:item/steam_switch" + } +} diff --git a/src/main/resources/assets/basesteam/items/steam_tank.json b/src/main/resources/assets/basesteam/items/steam_tank.json new file mode 100644 index 0000000..8a26e43 --- /dev/null +++ b/src/main/resources/assets/basesteam/items/steam_tank.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "basesteam:item/steam_tank" + } +} diff --git a/src/main/resources/assets/basesteam/items/steam_track.json b/src/main/resources/assets/basesteam/items/steam_track.json new file mode 100644 index 0000000..50252ca --- /dev/null +++ b/src/main/resources/assets/basesteam/items/steam_track.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "basesteam:item/steam_track" + } +} diff --git a/src/main/resources/assets/basesteam/models/item/musket.json b/src/main/resources/assets/basesteam/models/item/musket.json index ca93dee..e57568a 100644 --- a/src/main/resources/assets/basesteam/models/item/musket.json +++ b/src/main/resources/assets/basesteam/models/item/musket.json @@ -1,25 +1,3 @@ { - "parent": "builtin/generated", - "overrides": [ - { - "predicate": { - "loaded": 0, - "loading": 0 - }, - "model": "basesteam:item/musket_unloaded" - }, - { - "predicate": { - "loaded": 0, - "loading": 1 - }, - "model": "basesteam:item/musket_loading" - }, - { - "predicate": { - "loaded": 1 - }, - "model": "basesteam:item/musket_loaded" - } - ] -} \ No newline at end of file + "parent": "basesteam:item/musket_unloaded" +} diff --git a/src/main/resources/assets/minecraft/atlases/items.json b/src/main/resources/assets/minecraft/atlases/items.json new file mode 100644 index 0000000..79b6fa5 --- /dev/null +++ b/src/main/resources/assets/minecraft/atlases/items.json @@ -0,0 +1,5 @@ +{ + "sources": [ + { "type": "single", "resource": "basesteam:entity/drill_bit" } + ] +} diff --git a/src/main/resources/data/basesteam/recipe/blackpowder_cartridge.json b/src/main/resources/data/basesteam/recipe/blackpowder_cartridge.json index 3be805a..d1bdb82 100644 --- a/src/main/resources/data/basesteam/recipe/blackpowder_cartridge.json +++ b/src/main/resources/data/basesteam/recipe/blackpowder_cartridge.json @@ -6,15 +6,9 @@ "p" ], "key": { - "L": { - "tag": "forge:nuggets/lead" - }, - "g": { - "item": "minecraft:gunpowder" - }, - "p": { - "item": "minecraft:paper" - } + "L": "#forge:nuggets/lead", + "g": "minecraft:gunpowder", + "p": "minecraft:paper" }, "result": { "id": "basesteam:blackpowder_cartridge" diff --git a/src/main/resources/data/basesteam/recipe/crushing_steam_boiler_coal.json b/src/main/resources/data/basesteam/recipe/crushing_steam_boiler_coal.json index 3dc84d1..828740d 100644 --- a/src/main/resources/data/basesteam/recipe/crushing_steam_boiler_coal.json +++ b/src/main/resources/data/basesteam/recipe/crushing_steam_boiler_coal.json @@ -1,8 +1,6 @@ { "type": "basemetals:crushing", - "ingredient": { - "item": "basesteam:steam_boiler_coal" - }, + "ingredient": "basesteam:steam_boiler_coal", "result": { "id": "basesteam:steam_governor", "count": 2 diff --git a/src/main/resources/data/basesteam/recipe/crushing_steam_boiler_electric.json b/src/main/resources/data/basesteam/recipe/crushing_steam_boiler_electric.json index 5975cb1..035f553 100644 --- a/src/main/resources/data/basesteam/recipe/crushing_steam_boiler_electric.json +++ b/src/main/resources/data/basesteam/recipe/crushing_steam_boiler_electric.json @@ -1,8 +1,6 @@ { "type": "basemetals:crushing", - "ingredient": { - "item": "basesteam:steam_boiler_electric" - }, + "ingredient": "basesteam:steam_boiler_electric", "result": { "id": "basesteam:steam_governor", "count": 2 diff --git a/src/main/resources/data/basesteam/recipe/crushing_steam_boiler_geothermal.json b/src/main/resources/data/basesteam/recipe/crushing_steam_boiler_geothermal.json index 088b861..7a42c7e 100644 --- a/src/main/resources/data/basesteam/recipe/crushing_steam_boiler_geothermal.json +++ b/src/main/resources/data/basesteam/recipe/crushing_steam_boiler_geothermal.json @@ -1,8 +1,6 @@ { "type": "basemetals:crushing", - "ingredient": { - "item": "basesteam:steam_boiler_geothermal" - }, + "ingredient": "basesteam:steam_boiler_geothermal", "result": { "id": "basesteam:steam_governor", "count": 2 diff --git a/src/main/resources/data/basesteam/recipe/crushing_steam_crusher.json b/src/main/resources/data/basesteam/recipe/crushing_steam_crusher.json index 6605395..b35d962 100644 --- a/src/main/resources/data/basesteam/recipe/crushing_steam_crusher.json +++ b/src/main/resources/data/basesteam/recipe/crushing_steam_crusher.json @@ -1,8 +1,6 @@ { "type": "basemetals:crushing", - "ingredient": { - "item": "basesteam:steam_crusher" - }, + "ingredient": "basesteam:steam_crusher", "result": { "id": "basesteam:steam_governor", "count": 2 diff --git a/src/main/resources/data/basesteam/recipe/crushing_steam_drill.json b/src/main/resources/data/basesteam/recipe/crushing_steam_drill.json index fbe25f9..eff9679 100644 --- a/src/main/resources/data/basesteam/recipe/crushing_steam_drill.json +++ b/src/main/resources/data/basesteam/recipe/crushing_steam_drill.json @@ -1,8 +1,6 @@ { "type": "basemetals:crushing", - "ingredient": { - "item": "basesteam:steam_drill" - }, + "ingredient": "basesteam:steam_drill", "result": { "id": "basesteam:steam_governor", "count": 2 diff --git a/src/main/resources/data/basesteam/recipe/crushing_steam_elevator.json b/src/main/resources/data/basesteam/recipe/crushing_steam_elevator.json index 85317da..f5b5e76 100644 --- a/src/main/resources/data/basesteam/recipe/crushing_steam_elevator.json +++ b/src/main/resources/data/basesteam/recipe/crushing_steam_elevator.json @@ -1,8 +1,6 @@ { "type": "basemetals:crushing", - "ingredient": { - "item": "basesteam:steam_elevator" - }, + "ingredient": "basesteam:steam_elevator", "result": { "id": "basesteam:steam_governor", "count": 3 diff --git a/src/main/resources/data/basesteam/recipe/crushing_steam_furnace.json b/src/main/resources/data/basesteam/recipe/crushing_steam_furnace.json index 8eaecb9..6aa4f54 100644 --- a/src/main/resources/data/basesteam/recipe/crushing_steam_furnace.json +++ b/src/main/resources/data/basesteam/recipe/crushing_steam_furnace.json @@ -1,8 +1,6 @@ { "type": "basemetals:crushing", - "ingredient": { - "item": "basesteam:steam_furnace" - }, + "ingredient": "basesteam:steam_furnace", "result": { "id": "basesteam:steam_governor", "count": 2 diff --git a/src/main/resources/data/basesteam/recipe/crushing_steam_governor.json b/src/main/resources/data/basesteam/recipe/crushing_steam_governor.json index ed3f560..4e41cbe 100644 --- a/src/main/resources/data/basesteam/recipe/crushing_steam_governor.json +++ b/src/main/resources/data/basesteam/recipe/crushing_steam_governor.json @@ -1,8 +1,6 @@ { "type": "basemetals:crushing", - "ingredient": { - "item": "basesteam:steam_governor" - }, + "ingredient": "basesteam:steam_governor", "result": { "id": "basepower:sprocket", "count": 2 diff --git a/src/main/resources/data/basesteam/recipe/crushing_steam_tank.json b/src/main/resources/data/basesteam/recipe/crushing_steam_tank.json index db592dc..790b36e 100644 --- a/src/main/resources/data/basesteam/recipe/crushing_steam_tank.json +++ b/src/main/resources/data/basesteam/recipe/crushing_steam_tank.json @@ -1,8 +1,6 @@ { "type": "basemetals:crushing", - "ingredient": { - "item": "basesteam:steam_tank" - }, + "ingredient": "basesteam:steam_tank", "result": { "id": "basesteam:steam_governor" }, diff --git a/src/main/resources/data/basesteam/recipe/musket_left_normal.json b/src/main/resources/data/basesteam/recipe/musket_left_normal.json index 7221dfc..0078406 100644 --- a/src/main/resources/data/basesteam/recipe/musket_left_normal.json +++ b/src/main/resources/data/basesteam/recipe/musket_left_normal.json @@ -5,15 +5,9 @@ "w " ], "key": { - "f": { - "item": "minecraft:flint_and_steel" - }, - "s": { - "tag": "forge:ingots/steel" - }, - "w": { - "tag": "minecraft:planks" - } + "f": "minecraft:flint_and_steel", + "s": "#forge:ingots/steel", + "w": "#minecraft:planks" }, "result": { "id": "basesteam:musket" diff --git a/src/main/resources/data/basesteam/recipe/musket_left_tech.json b/src/main/resources/data/basesteam/recipe/musket_left_tech.json index 6667b33..65b1c20 100644 --- a/src/main/resources/data/basesteam/recipe/musket_left_tech.json +++ b/src/main/resources/data/basesteam/recipe/musket_left_tech.json @@ -5,15 +5,9 @@ "w " ], "key": { - "f": { - "item": "minecraft:flint_and_steel" - }, - "s": { - "tag": "forge:ingots/steel" - }, - "w": { - "tag": "minecraft:planks" - } + "f": "minecraft:flint_and_steel", + "s": "#forge:ingots/steel", + "w": "#minecraft:planks" }, "result": { "id": "basesteam:musket" diff --git a/src/main/resources/data/basesteam/recipe/musket_right_normal.json b/src/main/resources/data/basesteam/recipe/musket_right_normal.json index 794f2d2..695825b 100644 --- a/src/main/resources/data/basesteam/recipe/musket_right_normal.json +++ b/src/main/resources/data/basesteam/recipe/musket_right_normal.json @@ -5,15 +5,9 @@ " w" ], "key": { - "f": { - "item": "minecraft:flint_and_steel" - }, - "s": { - "tag": "forge:ingots/steel" - }, - "w": { - "tag": "minecraft:planks" - } + "f": "minecraft:flint_and_steel", + "s": "#forge:ingots/steel", + "w": "#minecraft:planks" }, "result": { "id": "basesteam:musket" diff --git a/src/main/resources/data/basesteam/recipe/musket_right_tech.json b/src/main/resources/data/basesteam/recipe/musket_right_tech.json index a04b48c..65491d3 100644 --- a/src/main/resources/data/basesteam/recipe/musket_right_tech.json +++ b/src/main/resources/data/basesteam/recipe/musket_right_tech.json @@ -5,15 +5,9 @@ " w" ], "key": { - "f": { - "item": "minecraft:flint_and_steel" - }, - "s": { - "tag": "forge:ingots/steel" - }, - "w": { - "tag": "minecraft:planks" - } + "f": "minecraft:flint_and_steel", + "s": "#forge:ingots/steel", + "w": "#minecraft:planks" }, "result": { "id": "basesteam:musket" diff --git a/src/main/resources/data/basesteam/recipe/steam_boiler_coal.json b/src/main/resources/data/basesteam/recipe/steam_boiler_coal.json index 662396d..4f63c3a 100644 --- a/src/main/resources/data/basesteam/recipe/steam_boiler_coal.json +++ b/src/main/resources/data/basesteam/recipe/steam_boiler_coal.json @@ -5,18 +5,10 @@ "pmp" ], "key": { - "X": { - "tag": "forge:conduits/steam" - }, - "g": { - "tag": "forge:governors" - }, - "p": { - "tag": "forge:plates/iron" - }, - "m": { - "tag": "forge:frames/steel" - } + "X": "#forge:conduits/steam", + "g": "#forge:governors", + "p": "#forge:plates/iron", + "m": "#forge:frames/steel" }, "result": { "id": "basesteam:steam_boiler_coal" diff --git a/src/main/resources/data/basesteam/recipe/steam_boiler_electric.json b/src/main/resources/data/basesteam/recipe/steam_boiler_electric.json index 0c3ae8a..dfc5a1b 100644 --- a/src/main/resources/data/basesteam/recipe/steam_boiler_electric.json +++ b/src/main/resources/data/basesteam/recipe/steam_boiler_electric.json @@ -6,21 +6,11 @@ "pmp" ], "key": { - "X": { - "tag": "forge:wires" - }, - "Y": { - "item": "minecraft:bucket" - }, - "g": { - "tag": "forge:governors" - }, - "p": { - "tag": "forge:plates/iron" - }, - "m": { - "tag": "forge:frames/steel" - } + "X": "#forge:wires", + "Y": "minecraft:bucket", + "g": "#forge:governors", + "p": "#forge:plates/iron", + "m": "#forge:frames/steel" }, "result": { "id": "basesteam:steam_boiler_electric" diff --git a/src/main/resources/data/basesteam/recipe/steam_boiler_geothermal.json b/src/main/resources/data/basesteam/recipe/steam_boiler_geothermal.json index 1a5f586..d93bc7f 100644 --- a/src/main/resources/data/basesteam/recipe/steam_boiler_geothermal.json +++ b/src/main/resources/data/basesteam/recipe/steam_boiler_geothermal.json @@ -6,21 +6,11 @@ "pmp" ], "key": { - "X": { - "tag": "forge:conduits/steam" - }, - "Y": { - "item": "minecraft:obsidian" - }, - "g": { - "tag": "forge:governors" - }, - "p": { - "tag": "forge:plates/iron" - }, - "m": { - "tag": "forge:frames/steel" - } + "X": "#forge:conduits/steam", + "Y": "minecraft:obsidian", + "g": "#forge:governors", + "p": "#forge:plates/iron", + "m": "#forge:frames/steel" }, "result": { "id": "basesteam:steam_boiler_geothermal" diff --git a/src/main/resources/data/basesteam/recipe/steam_boiler_oil.json b/src/main/resources/data/basesteam/recipe/steam_boiler_oil.json index 544b0c2..e3e388d 100644 --- a/src/main/resources/data/basesteam/recipe/steam_boiler_oil.json +++ b/src/main/resources/data/basesteam/recipe/steam_boiler_oil.json @@ -6,21 +6,11 @@ "pmp" ], "key": { - "X": { - "item": "minecraft:furnace" - }, - "Y": { - "item": "minecraft:bucket" - }, - "g": { - "tag": "forge:governors" - }, - "p": { - "tag": "forge:plates/iron" - }, - "m": { - "tag": "forge:frames/steel" - } + "X": "minecraft:furnace", + "Y": "minecraft:bucket", + "g": "#forge:governors", + "p": "#forge:plates/iron", + "m": "#forge:frames/steel" }, "result": { "id": "basesteam:steam_boiler_oil" diff --git a/src/main/resources/data/basesteam/recipe/steam_crusher.json b/src/main/resources/data/basesteam/recipe/steam_crusher.json index 96ebf20..3712bcd 100644 --- a/src/main/resources/data/basesteam/recipe/steam_crusher.json +++ b/src/main/resources/data/basesteam/recipe/steam_crusher.json @@ -6,21 +6,11 @@ "pmp" ], "key": { - "X": { - "item": "minecraft:piston" - }, - "Y": { - "tag": "forge:storage_blocks/steel" - }, - "g": { - "tag": "forge:governors" - }, - "p": { - "tag": "forge:plates/iron" - }, - "m": { - "tag": "forge:frames/steel" - } + "X": "minecraft:piston", + "Y": "#forge:storage_blocks/steel", + "g": "#forge:governors", + "p": "#forge:plates/iron", + "m": "#forge:frames/steel" }, "result": { "id": "basesteam:steam_crusher" diff --git a/src/main/resources/data/basesteam/recipe/steam_drill.json b/src/main/resources/data/basesteam/recipe/steam_drill.json index b0dba7d..918488e 100644 --- a/src/main/resources/data/basesteam/recipe/steam_drill.json +++ b/src/main/resources/data/basesteam/recipe/steam_drill.json @@ -5,18 +5,10 @@ "pmp" ], "key": { - "X": { - "item": "basesteam:steam_drill_bit" - }, - "g": { - "tag": "forge:governors" - }, - "p": { - "tag": "forge:plates/iron" - }, - "m": { - "tag": "forge:frames/steel" - } + "X": "basesteam:steam_drill_bit", + "g": "#forge:governors", + "p": "#forge:plates/iron", + "m": "#forge:frames/steel" }, "result": { "id": "basesteam:steam_drill" diff --git a/src/main/resources/data/basesteam/recipe/steam_drill_bit.json b/src/main/resources/data/basesteam/recipe/steam_drill_bit.json index f75189c..88fbf60 100644 --- a/src/main/resources/data/basesteam/recipe/steam_drill_bit.json +++ b/src/main/resources/data/basesteam/recipe/steam_drill_bit.json @@ -6,15 +6,9 @@ "did" ], "key": { - "g": { - "tag": "forge:sprockets" - }, - "i": { - "tag": "forge:ingots/steel" - }, - "d": { - "tag": "forge:gems/diamond" - } + "g": "#forge:sprockets", + "i": "#forge:ingots/steel", + "d": "#forge:gems/diamond" }, "result": { "id": "basesteam:steam_drill_bit" diff --git a/src/main/resources/data/basesteam/recipe/steam_drill_diamond_pickaxe_normal.json b/src/main/resources/data/basesteam/recipe/steam_drill_diamond_pickaxe_normal.json index 296056f..209d7b4 100644 --- a/src/main/resources/data/basesteam/recipe/steam_drill_diamond_pickaxe_normal.json +++ b/src/main/resources/data/basesteam/recipe/steam_drill_diamond_pickaxe_normal.json @@ -5,18 +5,10 @@ "pmp" ], "key": { - "X": { - "item": "minecraft:diamond_pickaxe" - }, - "g": { - "tag": "forge:governors" - }, - "p": { - "tag": "forge:plates/iron" - }, - "m": { - "tag": "forge:frames/steel" - } + "X": "minecraft:diamond_pickaxe", + "g": "#forge:governors", + "p": "#forge:plates/iron", + "m": "#forge:frames/steel" }, "result": { "id": "basesteam:steam_drill" diff --git a/src/main/resources/data/basesteam/recipe/steam_elevator.json b/src/main/resources/data/basesteam/recipe/steam_elevator.json index 82709ed..2fa1822 100644 --- a/src/main/resources/data/basesteam/recipe/steam_elevator.json +++ b/src/main/resources/data/basesteam/recipe/steam_elevator.json @@ -6,21 +6,11 @@ "pmp" ], "key": { - "X": { - "item": "minecraft:piston" - }, - "Y": { - "tag": "forge:sprockets" - }, - "g": { - "tag": "forge:governors" - }, - "p": { - "tag": "forge:plates/iron" - }, - "m": { - "tag": "forge:frames/steel" - } + "X": "minecraft:piston", + "Y": "#forge:sprockets", + "g": "#forge:governors", + "p": "#forge:plates/iron", + "m": "#forge:frames/steel" }, "result": { "id": "basesteam:steam_elevator" diff --git a/src/main/resources/data/basesteam/recipe/steam_furnace.json b/src/main/resources/data/basesteam/recipe/steam_furnace.json index e7006eb..1c14873 100644 --- a/src/main/resources/data/basesteam/recipe/steam_furnace.json +++ b/src/main/resources/data/basesteam/recipe/steam_furnace.json @@ -5,18 +5,10 @@ "pmp" ], "key": { - "X": { - "item": "minecraft:furnace" - }, - "g": { - "tag": "forge:governors" - }, - "p": { - "tag": "forge:plates/iron" - }, - "m": { - "tag": "forge:frames/steel" - } + "X": "minecraft:furnace", + "g": "#forge:governors", + "p": "#forge:plates/iron", + "m": "#forge:frames/steel" }, "result": { "id": "basesteam:steam_furnace" diff --git a/src/main/resources/data/basesteam/recipe/steam_governor_apocalyptic.json b/src/main/resources/data/basesteam/recipe/steam_governor_apocalyptic.json index cb9bfc8..10a28c5 100644 --- a/src/main/resources/data/basesteam/recipe/steam_governor_apocalyptic.json +++ b/src/main/resources/data/basesteam/recipe/steam_governor_apocalyptic.json @@ -6,18 +6,10 @@ "btb" ], "key": { - "t": { - "tag": "forge:sprockets" - }, - "s": { - "tag": "forge:rods" - }, - "r": { - "tag": "forge:rods" - }, - "b": { - "tag": "forge:ingots/brass" - } + "t": "#forge:sprockets", + "s": "#forge:rods", + "r": "#forge:rods", + "b": "#forge:ingots/brass" }, "result": { "id": "basesteam:steam_governor" diff --git a/src/main/resources/data/basesteam/recipe/steam_governor_normal.json b/src/main/resources/data/basesteam/recipe/steam_governor_normal.json index ebe3123..389ad1f 100644 --- a/src/main/resources/data/basesteam/recipe/steam_governor_normal.json +++ b/src/main/resources/data/basesteam/recipe/steam_governor_normal.json @@ -6,18 +6,10 @@ "btb" ], "key": { - "t": { - "tag": "forge:nuggets/iron" - }, - "s": { - "item": "minecraft:stick" - }, - "r": { - "item": "minecraft:stick" - }, - "b": { - "tag": "forge:ingots/brass" - } + "t": "#forge:nuggets/iron", + "s": "minecraft:stick", + "r": "minecraft:stick", + "b": "#forge:ingots/brass" }, "result": { "id": "basesteam:steam_governor" diff --git a/src/main/resources/data/basesteam/recipe/steam_governor_tech.json b/src/main/resources/data/basesteam/recipe/steam_governor_tech.json index 62e0121..2646914 100644 --- a/src/main/resources/data/basesteam/recipe/steam_governor_tech.json +++ b/src/main/resources/data/basesteam/recipe/steam_governor_tech.json @@ -6,18 +6,10 @@ "btb" ], "key": { - "t": { - "tag": "forge:sprockets" - }, - "s": { - "tag": "forge:rods" - }, - "r": { - "tag": "forge:rods" - }, - "b": { - "tag": "forge:ingots/brass" - } + "t": "#forge:sprockets", + "s": "#forge:rods", + "r": "#forge:rods", + "b": "#forge:ingots/brass" }, "result": { "id": "basesteam:steam_governor" diff --git a/src/main/resources/data/basesteam/recipe/steam_pipe.json b/src/main/resources/data/basesteam/recipe/steam_pipe.json index e36a495..93fd884 100644 --- a/src/main/resources/data/basesteam/recipe/steam_pipe.json +++ b/src/main/resources/data/basesteam/recipe/steam_pipe.json @@ -6,9 +6,7 @@ "xxx" ], "key": { - "x": { - "tag": "forge:ingots/brass" - } + "x": "#forge:ingots/brass" }, "result": { "id": "basesteam:steam_pipe", diff --git a/src/main/resources/data/basesteam/recipe/steam_pump.json b/src/main/resources/data/basesteam/recipe/steam_pump.json index 8caa905..f59e03a 100644 --- a/src/main/resources/data/basesteam/recipe/steam_pump.json +++ b/src/main/resources/data/basesteam/recipe/steam_pump.json @@ -6,21 +6,11 @@ "pmp" ], "key": { - "X": { - "item": "minecraft:piston" - }, - "Y": { - "item": "minecraft:bucket" - }, - "g": { - "tag": "forge:governors" - }, - "p": { - "tag": "forge:plates/iron" - }, - "m": { - "tag": "forge:frames/steel" - } + "X": "minecraft:piston", + "Y": "minecraft:bucket", + "g": "#forge:governors", + "p": "#forge:plates/iron", + "m": "#forge:frames/steel" }, "result": { "id": "basesteam:steam_pump" diff --git a/src/main/resources/data/basesteam/recipe/steam_still.json b/src/main/resources/data/basesteam/recipe/steam_still.json index 5749711..a7acef2 100644 --- a/src/main/resources/data/basesteam/recipe/steam_still.json +++ b/src/main/resources/data/basesteam/recipe/steam_still.json @@ -6,21 +6,11 @@ "pmp" ], "key": { - "X": { - "item": "minecraft:bucket" - }, - "Y": { - "item": "minecraft:bucket" - }, - "g": { - "tag": "forge:governors" - }, - "p": { - "tag": "forge:plates/iron" - }, - "m": { - "tag": "forge:frames/steel" - } + "X": "minecraft:bucket", + "Y": "minecraft:bucket", + "g": "#forge:governors", + "p": "#forge:plates/iron", + "m": "#forge:frames/steel" }, "result": { "id": "basesteam:steam_still" diff --git a/src/main/resources/data/basesteam/recipe/steam_switch.json b/src/main/resources/data/basesteam/recipe/steam_switch.json index a3984b5..5f07f88 100644 --- a/src/main/resources/data/basesteam/recipe/steam_switch.json +++ b/src/main/resources/data/basesteam/recipe/steam_switch.json @@ -5,15 +5,9 @@ "pfp" ], "key": { - "L": { - "item": "minecraft:lever" - }, - "p": { - "tag": "forge:conduits/steam" - }, - "f": { - "tag": "forge:frames/steel" - } + "L": "minecraft:lever", + "p": "#forge:conduits/steam", + "f": "#forge:frames/steel" }, "result": { "id": "basesteam:steam_switch" diff --git a/src/main/resources/data/basesteam/recipe/steam_tank.json b/src/main/resources/data/basesteam/recipe/steam_tank.json index 6c55bb7..462f6e6 100644 --- a/src/main/resources/data/basesteam/recipe/steam_tank.json +++ b/src/main/resources/data/basesteam/recipe/steam_tank.json @@ -6,15 +6,9 @@ "xxx" ], "key": { - "x": { - "tag": "forge:plates/copper" - }, - "p": { - "tag": "forge:conduits/steam" - }, - "g": { - "tag": "forge:governors" - } + "x": "#forge:plates/copper", + "p": "#forge:conduits/steam", + "g": "#forge:governors" }, "result": { "id": "basesteam:steam_tank" diff --git a/src/main/resources/data/basesteam/recipe/steam_track.json b/src/main/resources/data/basesteam/recipe/steam_track.json index 97ee6ee..d69668e 100644 --- a/src/main/resources/data/basesteam/recipe/steam_track.json +++ b/src/main/resources/data/basesteam/recipe/steam_track.json @@ -1,12 +1,8 @@ { "type": "minecraft:crafting_shapeless", "ingredients": [ - { - "item": "basesteam:steam_pipe" - }, - { - "item": "basepower:steel_frame" - } + "basesteam:steam_pipe", + "basepower:steel_frame" ], "result": { "id": "basesteam:steam_track" diff --git a/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta index 25eee2f..ba78a5d 100644 --- a/src/main/resources/pack.mcmeta +++ b/src/main/resources/pack.mcmeta @@ -1,7 +1,7 @@ { "pack":{ - "pack_format":34, - "supported_formats":{"min_inclusive":34,"max_inclusive":48}, + "min_format":[94,1], + "max_format":[94,1], "description":"baseSteam" } } diff --git a/src/test/java/zone/moddev/mc/basesteam/blocks/DrillBitPersistenceTest.java b/src/test/java/zone/moddev/mc/basesteam/blocks/DrillBitPersistenceTest.java index 33a7102..10ce728 100644 --- a/src/test/java/zone/moddev/mc/basesteam/blocks/DrillBitPersistenceTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/blocks/DrillBitPersistenceTest.java @@ -21,14 +21,16 @@ public class DrillBitPersistenceTest { var registries = NativeTestBootstrap.registries(); CompoundTag saved = tile.saveWithoutMetadata(registries); - assertEquals(direction.get3DDataValue(), saved.getByte("dir")); + assertEquals(direction.get3DDataValue(), saved.getByteOr("dir", (byte)0)); DrillBitTileEntity restored = new DrillBitTileEntity(net.minecraft.core.BlockPos.ZERO, NativeMachineFixture.state("drillbit")); - restored.loadWithComponents(saved, registries); - assertEquals(direction.get3DDataValue(), restored.getUpdateTag(registries).getByte("dir")); + restored.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, registries, saved)); + assertEquals(direction.get3DDataValue(), restored.getUpdateTag(registries).getByteOr("dir", (byte)0)); DrillBitTileEntity client = new DrillBitTileEntity(net.minecraft.core.BlockPos.ZERO, NativeMachineFixture.state("drillbit")); - client.handleUpdateTag(tile.getUpdateTag(registries), registries); - assertEquals(direction.get3DDataValue(), client.getUpdateTag(registries).getByte("dir")); + client.handleUpdateTag(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, registries, tile.getUpdateTag(registries)), registries); + assertEquals(direction.get3DDataValue(), client.getUpdateTag(registries).getByteOr("dir", (byte)0)); } } @@ -36,10 +38,12 @@ public class DrillBitPersistenceTest { CompoundTag tag = new CompoundTag(); tag.putByte("d", (byte) Direction.EAST.get3DDataValue()); DrillBitTileEntity tile = new DrillBitTileEntity(net.minecraft.core.BlockPos.ZERO, NativeMachineFixture.state("drillbit")); - tile.loadWithComponents(tag, NativeTestBootstrap.registries()); + tile.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, NativeTestBootstrap.registries(), tag)); assertEquals(Direction.Axis.X, tile.getDirection()); tag.putByte("dir", (byte) Direction.NORTH.get3DDataValue()); - tile.loadWithComponents(tag, NativeTestBootstrap.registries()); + tile.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, NativeTestBootstrap.registries(), tag)); assertEquals(Direction.Axis.Z, tile.getDirection()); } @@ -47,7 +51,8 @@ public class DrillBitPersistenceTest { CompoundTag tag = new CompoundTag(); tag.putByte("dir", (byte) 127); DrillBitTileEntity tile = new DrillBitTileEntity(net.minecraft.core.BlockPos.ZERO, NativeMachineFixture.state("drillbit")); - tile.loadWithComponents(tag, NativeTestBootstrap.registries()); - assertEquals(Direction.DOWN.get3DDataValue(), tile.getUpdateTag(NativeTestBootstrap.registries()).getByte("dir")); + tile.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, NativeTestBootstrap.registries(), tag)); + assertEquals(Direction.DOWN.get3DDataValue(), tile.getUpdateTag(NativeTestBootstrap.registries()).getByteOr("dir", (byte)0)); } } diff --git a/src/test/java/zone/moddev/mc/basesteam/graphics/DrillBitRendererTest.java b/src/test/java/zone/moddev/mc/basesteam/graphics/DrillBitRendererTest.java index d2959e8..c220fcf 100644 --- a/src/test/java/zone/moddev/mc/basesteam/graphics/DrillBitRendererTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/graphics/DrillBitRendererTest.java @@ -95,9 +95,15 @@ private static class RecordingBuffer implements VertexConsumer { vertex.color[0] = red; vertex.color[1] = green; vertex.color[2] = blue; vertex.color[3] = alpha; return this; } + @Override public VertexConsumer setColor(int color) { + return setColor((color >>> 16) & 255, (color >>> 8) & 255, color & 255, color >>> 24); + } @Override public VertexConsumer setUv(float u, float v) { vertex.u = u; vertex.v = v; return this; } @Override public VertexConsumer setUv1(int u, int v) { vertex.overlay = u | v << 16; return this; } @Override public VertexConsumer setUv2(int u, int v) { vertex.light = u | v << 16; return this; } + @Override public VertexConsumer setLineWidth(float width) { + throw new AssertionError("Drill geometry must submit quads, not line vertices"); + } @Override public VertexConsumer setNormal(float x, float y, float z) { vertex.normal[0] = x; vertex.normal[1] = y; vertex.normal[2] = z; return this; diff --git a/src/test/java/zone/moddev/mc/basesteam/gui/GUIHelperTest.java b/src/test/java/zone/moddev/mc/basesteam/gui/GUIHelperTest.java index e18b274..d270462 100644 --- a/src/test/java/zone/moddev/mc/basesteam/gui/GUIHelperTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/gui/GUIHelperTest.java @@ -1,7 +1,10 @@ package zone.moddev.mc.basesteam.gui; -import com.mojang.blaze3d.vertex.PoseStack; -import org.joml.Vector3f; +import org.joml.Matrix3x2fStack; +import org.joml.Matrix3x2f; +import org.joml.Vector2f; +import cyano.poweradvantage.api.simple.SimpleMachineGUI; +import cyano.poweradvantage.api.simple.SimpleMachineGUI.GUIContainer; import org.junit.Test; import static org.junit.Assert.*; @@ -16,14 +19,12 @@ public class GUIHelperTest { @Test public void needleSweepsLeftToRightThroughTheSamePivot() { for (float value : new float[] {0F, 0.25F, 0.5F, 0.75F, 1F, Float.NaN}) { - PoseStack pose = new PoseStack(); - GUIHelper.positionNeedle(pose, 44F, 40F, 1F, value); - var transform = pose.last().pose(); - var pivot = transform.transformPosition(new Vector3f(3.5F, 17.5F, 0F)); + Matrix3x2fStack pose = new Matrix3x2fStack(2); + GUIHelper.positionNeedle(pose, 44F, 40F, value); + var pivot = pose.transformPosition(new Vector2f(3.5F, 17.5F)); assertEquals(44F, pivot.x, 0.0001F); assertEquals(40F, pivot.y, 0.0001F); - assertEquals(1F, pivot.z, 0.0001F); - var tip = transform.transformPosition(new Vector3f(3.5F, 0F, 0F)); + var tip = pose.transformPosition(new Vector2f(3.5F, 0F)); // Independent geometry from the previous native vertex implementation. double legacyAngle = (1D - (Float.isNaN(value) ? 0D : value)) * Math.PI; assertEquals(44D + Math.cos(legacyAngle) * 17.5D, tip.x, 0.0001D); @@ -36,4 +37,67 @@ public class GUIHelperTest { assertEquals(0F, GUIHelper.displayLevel(value), 0F); } } + + @Test public void screenAwareNeedleSubmitsTheOriginalRegionAndRestoresTheMatrixEvenOnFailure() throws Exception { + var field = Class.forName("sun.misc.Unsafe").getDeclaredField("theUnsafe"); + field.setAccessible(true); + var unsafe = field.get(null); + DrawingContainer container = (DrawingContainer) unsafe.getClass().getMethod("allocateInstance", Class.class) + .invoke(unsafe, DrawingContainer.class); + container.matrices = new Matrix3x2fStack(2); + container.matrices.translate(10F, 20F); + Matrix3x2f before = new Matrix3x2f(container.matrices); + GUIHelper.drawNeedle(container, 44F, 40F, 1F, 0.75F); + assertEquals(before, container.matrices); + assertArrayEquals(new int[] {0, 0, 176, 0, 7, 35}, container.region); + var pivot = container.submitted.transformPosition(new Vector2f(3.5F, 17.5F)); + assertEquals(54F, pivot.x, 0.0001F); + assertEquals(60F, pivot.y, 0.0001F); + container.refuse = true; + assertThrows(IllegalStateException.class, () -> GUIHelper.drawNeedle(container, 44F, 40F, 1F, 0.75F)); + assertEquals(before, container.matrices); + assertThrows("The legacy overload cannot draw outside the real decoration callback", IllegalStateException.class, + () -> GUIHelper.drawingContainer(container)); + assertThrows(IllegalStateException.class, () -> GUIHelper.drawingContainer(null)); + } + + @Test public void legacySignatureDelegatesFromTheNativeCurrentScreenWithoutImmediateGpuCalls() throws Exception { + var node = new org.objectweb.asm.tree.ClassNode(); + try (var input = GUIHelper.class.getResourceAsStream("GUIHelper.class")) { + assertNotNull(input); + new org.objectweb.asm.ClassReader(input).accept(node, 0); + } + var method = node.methods.stream().filter(value -> value.name.equals("drawNeedle") + && value.desc.equals("(FFFF)V")).findFirst().orElseThrow(); + boolean currentScreen = false, delegate = false; + for (var instruction : method.instructions) { + if (instruction instanceof org.objectweb.asm.tree.FieldInsnNode field) { + if (field.owner.equals("net/minecraft/client/Minecraft") && field.name.equals("screen")) currentScreen = true; + } + if (instruction instanceof org.objectweb.asm.tree.MethodInsnNode call) { + assertFalse(call.owner, call.owner.startsWith("com/mojang/blaze3d/")); + if (call.owner.equals(node.name) && call.name.equals("drawNeedle") + && call.desc.equals("(Lcyano/poweradvantage/api/simple/SimpleMachineGUI$GUIContainer;FFFF)V")) delegate = true; + } + } + assertTrue(currentScreen && delegate); + } + + private static final class DrawingContainer extends GUIContainer { + private Matrix3x2fStack matrices; + private Matrix3x2f submitted; + private int[] region; + private boolean refuse; + + private DrawingContainer(SimpleMachineGUI owner) { + owner.super((SimpleMachineGUI.Container) null, null, net.minecraft.network.chat.Component.empty()); + } + + @Override public Matrix3x2fStack getMatrices() { return matrices; } + @Override public void blit(int x, int y, int u, int v, int width, int height) { + submitted = new Matrix3x2f(matrices); + region = new int[] {x, y, u, v, width, height}; + if (refuse) throw new IllegalStateException("needle submission failed"); + } + } } diff --git a/src/test/java/zone/moddev/mc/basesteam/items/MusketPersistenceTest.java b/src/test/java/zone/moddev/mc/basesteam/items/MusketPersistenceTest.java index 154079c..ae34133 100644 --- a/src/test/java/zone/moddev/mc/basesteam/items/MusketPersistenceTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/items/MusketPersistenceTest.java @@ -25,19 +25,20 @@ public class MusketPersistenceTest { stack.setDamageValue(123); assertTrue(MusketItem.isNotLoaded(stack)); MusketItem.startLoad(stack); - assertEquals(2, data(stack).getByte("loaded")); + assertEquals(2, data(stack).getByteOr("loaded", (byte)0)); assertTrue(MusketItem.isAlmostLoaded(stack)); MusketItem.finishLoad(stack); - assertEquals(1, data(stack).getByte("loaded")); + assertEquals(1, data(stack).getByteOr("loaded", (byte)0)); assertTrue(MusketItem.isLoaded(stack.copy())); var registries = NativeTestBootstrap.registries(); - ItemStack restored = ItemStack.parse(registries, stack.save(registries)).orElseThrow(); + ItemStack restored = ItemStack.CODEC.parse(registries.createSerializationContext(net.minecraft.nbt.NbtOps.INSTANCE), + ItemStack.CODEC.encodeStart(registries.createSerializationContext(net.minecraft.nbt.NbtOps.INSTANCE), stack).getOrThrow()).getOrThrow(); assertTrue(MusketItem.isLoaded(restored)); assertEquals(123, restored.getDamageValue()); MusketItem.unload(stack); - assertEquals(0, data(stack).getByte("loaded")); + assertEquals(0, data(stack).getByteOr("loaded", (byte)0)); assertTrue(MusketItem.isNotLoaded(stack)); - assertEquals("legacy", data(stack).getString("owner")); + assertEquals("legacy", data(stack).getStringOr("owner", "")); assertEquals(123, stack.getDamageValue()); assertTrue(MusketItem.isLoaded(restored)); } diff --git a/src/test/java/zone/moddev/mc/basesteam/items/MusketTargetingContractTest.java b/src/test/java/zone/moddev/mc/basesteam/items/MusketTargetingContractTest.java index 6b1864f..a808697 100644 --- a/src/test/java/zone/moddev/mc/basesteam/items/MusketTargetingContractTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/items/MusketTargetingContractTest.java @@ -55,8 +55,8 @@ public class MusketTargetingContractTest { .findFirst().orElseThrow(() -> new AssertionError("Missing Minecraft's onUseTick override")); boolean clientGate = false, loadGate = false, sound = false; for (AbstractInsnNode instruction : useTick.instructions.toArray()) { - if (instruction instanceof FieldInsnNode field && field.name.equals("isClientSide")) clientGate = true; if (!(instruction instanceof MethodInsnNode call)) continue; + if (call.name.equals("isClientSide")) clientGate = true; if (call.name.equals("isNotLoaded")) loadGate = true; if (call.name.equals("playSound")) { assertTrue("Reload sound must be client-only and stop when loaded", clientGate && loadGate); diff --git a/src/test/java/zone/moddev/mc/basesteam/machines/CoalBoilerFillPersistenceTest.java b/src/test/java/zone/moddev/mc/basesteam/machines/CoalBoilerFillPersistenceTest.java index 8109450..9ec7afe 100644 --- a/src/test/java/zone/moddev/mc/basesteam/machines/CoalBoilerFillPersistenceTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/machines/CoalBoilerFillPersistenceTest.java @@ -177,12 +177,12 @@ private static SaveWitnessBoiler seeded(int amount) { private static void assertSaved(SaveWitnessBoiler boiler, int amount) { assertTrue("An accepted external fill must request saving without relying on a later tile tick", boiler.dirtyCalls > 0); - assertEquals(amount, boiler.savedTank.getInt("Amount")); + assertEquals(amount, boiler.savedTank.getIntOr("Amount", 0)); } private static void assertUnchangedSave(SaveWitnessBoiler boiler, int amount) { assertEquals(0, boiler.dirtyCalls); - assertEquals(amount, boiler.savedTank.getInt("Amount")); + assertEquals(amount, boiler.savedTank.getIntOr("Amount", 0)); } private static final class SaveWitnessBoiler extends CoalBoilerTileEntity { @@ -217,6 +217,6 @@ private static SaveWorld world(boolean remote) { } } private static final class SaveWorld extends net.minecraft.server.level.ServerLevel { - private SaveWorld() { super(null, null, null, null, null, null, null, false, 0L, java.util.Collections.emptyList(), false, null); } + private SaveWorld() { super(null, null, null, null, null, null, false, 0L, java.util.Collections.emptyList(), false, null); } } } diff --git a/src/test/java/zone/moddev/mc/basesteam/machines/RockCrusherReloadRedstoneTest.java b/src/test/java/zone/moddev/mc/basesteam/machines/RockCrusherReloadRedstoneTest.java index 35e0e9a..5c0e915 100644 --- a/src/test/java/zone/moddev/mc/basesteam/machines/RockCrusherReloadRedstoneTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/machines/RockCrusherReloadRedstoneTest.java @@ -84,7 +84,7 @@ private static final class ControlSample extends RuntimeException {} private static final class ControlWorld extends ServerLevel { boolean powered, stopAtFirstSample; int samples; - private ControlWorld() { super(null, null, null, null, null, null, null, false, 0L, java.util.Collections.emptyList(), false, null); } + private ControlWorld() { super(null, null, null, null, null, null, false, 0L, java.util.Collections.emptyList(), false, null); } @Override public boolean hasNeighborSignal(BlockPos pos) { samples++; if (stopAtFirstSample) throw new ControlSample(); diff --git a/src/test/java/zone/moddev/mc/basesteam/machines/SteamDrillPersistenceTest.java b/src/test/java/zone/moddev/mc/basesteam/machines/SteamDrillPersistenceTest.java index 474dca7..cc3c31b 100644 --- a/src/test/java/zone/moddev/mc/basesteam/machines/SteamDrillPersistenceTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/machines/SteamDrillPersistenceTest.java @@ -36,15 +36,18 @@ public class SteamDrillPersistenceTest { @Test public void retainsLegacyProgressUntilWorldIsAttached() { SteamDrillTileEntity drill = new SteamDrillTileEntity(net.minecraft.core.BlockPos.ZERO, NativeMachineFixture.state("steam_drill")); - drill.loadWithComponents(target(94), NativeTestBootstrap.registries()); + drill.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, NativeTestBootstrap.registries(), target(94))); drill.prepareDataFieldsForSync(); assertEquals(94, drill.getDataFieldArray()[1]); } @Test public void clearsPreviousProgressOnReadingAnUntargetedTile() { SteamDrillTileEntity drill = new SteamDrillTileEntity(net.minecraft.core.BlockPos.ZERO, NativeMachineFixture.state("steam_drill")); - drill.loadWithComponents(target(94), NativeTestBootstrap.registries()); - drill.loadWithComponents(new CompoundTag(), NativeTestBootstrap.registries()); + drill.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, NativeTestBootstrap.registries(), target(94))); + drill.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, NativeTestBootstrap.registries(), new CompoundTag())); drill.prepareDataFieldsForSync(); assertEquals(0, drill.getDataFieldArray()[1]); } @@ -57,7 +60,8 @@ public class SteamDrillPersistenceTest { state.putString("Name", "minecraft:stone"); tag.put("TargetState", state); SteamDrillTileEntity drill = new SteamDrillTileEntity(net.minecraft.core.BlockPos.ZERO, NativeMachineFixture.state("steam_drill")); - drill.loadWithComponents(tag, NativeTestBootstrap.registries()); + drill.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, NativeTestBootstrap.registries(), tag)); assertEquals(facing, field(drill, "savedFacing")); assertEquals(new BlockPos(2048, 80, 2049), field(drill, "targetBlockCoord")); assertEquals(state, field(drill, "savedTargetState")); @@ -71,11 +75,14 @@ public class SteamDrillPersistenceTest { SteamDrillTileEntity drill = new SteamDrillTileEntity(net.minecraft.core.BlockPos.ZERO, NativeMachineFixture.state("steam_drill")); CompoundTag tag = target(94); tag.putByte("Facing", (byte) Direction.UP.get3DDataValue()); - drill.loadWithComponents(tag, NativeTestBootstrap.registries()); - drill.loadWithComponents(target(12), NativeTestBootstrap.registries()); + drill.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, NativeTestBootstrap.registries(), tag)); + drill.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, NativeTestBootstrap.registries(), target(12))); assertNull(field(drill, "savedFacing")); tag.putByte("Facing", (byte) 127); - drill.loadWithComponents(tag, NativeTestBootstrap.registries()); + drill.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, NativeTestBootstrap.registries(), tag)); assertNull(field(drill, "savedFacing")); } @@ -90,7 +97,8 @@ public class SteamDrillPersistenceTest { items.add(stack); tag.put("Items", items); SteamDrillTileEntity drill = new SteamDrillTileEntity(net.minecraft.core.BlockPos.ZERO, NativeMachineFixture.state("steam_drill")); - drill.loadWithComponents(tag, NativeTestBootstrap.registries()); + drill.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, NativeTestBootstrap.registries(), tag)); assertEquals(37.5F, drill.getEnergy(Power.steam_power), 0.0F); ItemStack output = drill.getItem(4); assertEquals(net.minecraft.world.item.Items.COBBLESTONE, output.getItem()); @@ -110,9 +118,9 @@ public class SteamDrillPersistenceTest { assertNull(drill.addItem(different)); assertNull(drill.addItem(matching)); assertEquals(11, drill.getItem(0).getCount()); - assertEquals("first", drill.getItem(0).get(DataComponents.CUSTOM_DATA).copyTag().getString("source")); + assertEquals("first", drill.getItem(0).get(DataComponents.CUSTOM_DATA).copyTag().getStringOr("source", "")); assertEquals(4, drill.getItem(1).getCount()); - assertEquals("second", drill.getItem(1).get(DataComponents.CUSTOM_DATA).copyTag().getString("source")); + assertEquals("second", drill.getItem(1).get(DataComponents.CUSTOM_DATA).copyTag().getStringOr("source", "")); } @Test public void serializationNeverLooksUpTheWorldOrInfersNeighbours() throws Exception { @@ -130,14 +138,37 @@ public class SteamDrillPersistenceTest { if (!(instruction instanceof MethodInsnNode)) continue; MethodInsnNode call = (MethodInsnNode) instruction; assertFalse("Serialization must not reacquire a saving chunk: " + call.name, - call.name.equals("getFacing") || call.name.equals("inferFacing") - || call.name.equals("powerAdvantageWorld") || call.owner.startsWith("net/minecraft/world/")); + serializationTouchesWorld(call)); } } assertEquals(1, methods); assertTrue("Cached facing must still be serialized", cachedFacingReads > 0); } + @Test public void nativeOutputWriteAllowlistStillRejectsWorldAccessAndUnrelatedIoMethods() { + String output = "net/minecraft/world/level/storage/ValueOutput"; + assertFalse(serializationTouchesWorld(new MethodInsnNode(org.objectweb.asm.Opcodes.INVOKEINTERFACE, + output, "putShort", "(Ljava/lang/String;S)V", true))); + assertTrue(serializationTouchesWorld(new MethodInsnNode(org.objectweb.asm.Opcodes.INVOKEINTERFACE, + output, "child", "(Ljava/lang/String;)Lnet/minecraft/world/level/storage/ValueOutput;", true))); + assertTrue(serializationTouchesWorld(new MethodInsnNode(org.objectweb.asm.Opcodes.INVOKEVIRTUAL, + "net/minecraft/world/level/Level", "getBlockState", + "(Lnet/minecraft/core/BlockPos;)Lnet/minecraft/world/level/block/state/BlockState;", false))); + for (String method : new String[] {"getFacing", "inferFacing", "powerAdvantageWorld"}) + assertTrue(serializationTouchesWorld(new MethodInsnNode(org.objectweb.asm.Opcodes.INVOKEVIRTUAL, + "zone/moddev/mc/basesteam/machines/SteamDrillTileEntity", method, "()Ljava/lang/Object;", false))); + } + + private static boolean serializationTouchesWorld(MethodInsnNode call) { + if (call.name.equals("getFacing") || call.name.equals("inferFacing") || call.name.equals("powerAdvantageWorld")) return true; + if (!call.owner.startsWith("net/minecraft/world/")) return false; + if (!call.owner.equals("net/minecraft/world/level/storage/ValueOutput") + || call.getOpcode() != org.objectweb.asm.Opcodes.INVOKEINTERFACE) return true; + return !java.util.Set.of("putShort(Ljava/lang/String;S)V", "putByte(Ljava/lang/String;B)V", + "putInt(Ljava/lang/String;I)V", "store(Ljava/lang/String;Lcom/mojang/serialization/Codec;Ljava/lang/Object;)V") + .contains(call.name + call.desc); + } + @Test public void facinglessLegacyTargetsInferAllDirectionsWithoutAnAttachedWorld() throws Exception { Method infer = SteamDrillTileEntity.class.getDeclaredMethod("inferFacing"); infer.setAccessible(true); @@ -149,7 +180,8 @@ public class SteamDrillPersistenceTest { tag.putInt("targetY", target.getY()); tag.putInt("targetZ", target.getZ()); SteamDrillTileEntity drill = new SteamDrillTileEntity(origin, NativeMachineFixture.state("steam_drill")); - drill.loadWithComponents(tag, NativeTestBootstrap.registries()); + drill.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, NativeTestBootstrap.registries(), tag)); assertNull(field(drill, "savedFacing")); assertEquals(direction, infer.invoke(drill)); } diff --git a/src/test/java/zone/moddev/mc/basesteam/machines/SteamMachinePersistenceTest.java b/src/test/java/zone/moddev/mc/basesteam/machines/SteamMachinePersistenceTest.java index ec9f8b4..c0e4959 100644 --- a/src/test/java/zone/moddev/mc/basesteam/machines/SteamMachinePersistenceTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/machines/SteamMachinePersistenceTest.java @@ -8,7 +8,7 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.ForgeRegistry; import org.junit.BeforeClass; @@ -54,20 +54,24 @@ public class SteamMachinePersistenceTest { tag.putInt("BurnTime", 127); tag.putInt("BurnTimeTotal", 1600); CoalBoilerTileEntity coal = new CoalBoilerTileEntity(net.minecraft.core.BlockPos.ZERO, NativeMachineFixture.state("steam_boiler_coal")); - coal.loadWithComponents(tag, NativeTestBootstrap.registries()); + coal.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, NativeTestBootstrap.registries(), tag)); CompoundTag saved = coal.saveWithFullMetadata(NativeTestBootstrap.registries()); - assertEquals("basesteam:steam_boiler_coal", saved.getString("id")); - assertEquals(127, saved.getInt("BurnTime")); - assertEquals(1600, saved.getInt("BurnTimeTotal")); + assertEquals("basesteam:steam_boiler_coal", saved.getStringOr("id", "")); + assertEquals(127, saved.getIntOr("BurnTime", 0)); + assertEquals(1600, saved.getIntOr("BurnTimeTotal", 0)); CoalBoilerTileEntity restored = new CoalBoilerTileEntity(net.minecraft.core.BlockPos.ZERO, NativeMachineFixture.state("steam_boiler_coal")); - restored.loadWithComponents(saved, NativeTestBootstrap.registries()); + restored.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, NativeTestBootstrap.registries(), saved)); assertEquals(37.5F, restored.getEnergy(Power.steam_power), 0.0F); assertEquals(1500, restored.getTank().getFluidAmount()); tag.putFloat("Electricity", 60.0F); ElectricBoilerTileEntity electric = new ElectricBoilerTileEntity(net.minecraft.core.BlockPos.ZERO, NativeMachineFixture.state("steam_boiler_electric")); - electric.loadWithComponents(tag, NativeTestBootstrap.registries()); + electric.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, NativeTestBootstrap.registries(), tag)); ElectricBoilerTileEntity electricRestored = new ElectricBoilerTileEntity(net.minecraft.core.BlockPos.ZERO, NativeMachineFixture.state("steam_boiler_electric")); - electricRestored.loadWithComponents(electric.saveWithoutMetadata(NativeTestBootstrap.registries()), NativeTestBootstrap.registries()); + electricRestored.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, NativeTestBootstrap.registries(), electric.saveWithoutMetadata(NativeTestBootstrap.registries()))); assertEquals(37.5F, electricRestored.getEnergy(Power.steam_power), 0.0F); assertEquals(60.0F, electricRestored.getEnergy(ElectricBoilerTileEntity.ELECTRIC_POWER), 0.0F); assertEquals(1500, electricRestored.getTank().getFluidAmount()); @@ -78,21 +82,25 @@ public class SteamMachinePersistenceTest { tag.put("TankOut", water(750)); tag.putByte("NextPump", (byte) 11); SteamPumpTileEntity pump = new SteamPumpTileEntity(net.minecraft.core.BlockPos.ZERO, NativeMachineFixture.state("steam_pump")); - pump.loadWithComponents(tag, NativeTestBootstrap.registries()); + pump.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, NativeTestBootstrap.registries(), tag)); CompoundTag saved = pump.saveWithoutMetadata(NativeTestBootstrap.registries()); - assertEquals(750, saved.getCompound("Tank").getInt("Amount")); - assertEquals(11, saved.getByte("NextPump")); + assertEquals(750, saved.getCompoundOrEmpty("Tank").getIntOr("Amount", 0)); + assertEquals(11, saved.getByteOr("NextPump", (byte)0)); SteamPumpTileEntity pumpRestored = new SteamPumpTileEntity(net.minecraft.core.BlockPos.ZERO, NativeMachineFixture.state("steam_pump")); - pumpRestored.loadWithComponents(saved, NativeTestBootstrap.registries()); + pumpRestored.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, NativeTestBootstrap.registries(), saved)); assertEquals(750, pumpRestored.getTank().getFluidAmount()); assertEquals(37.5F, pumpRestored.getEnergy(Power.steam_power), 0.0F); tag.putFloat("Tempoerature", 412.5F); GeothermalBoilerTileEntity geothermal = new GeothermalBoilerTileEntity(net.minecraft.core.BlockPos.ZERO, NativeMachineFixture.state("steam_boiler_geothermal")); - geothermal.loadWithComponents(tag, NativeTestBootstrap.registries()); - assertEquals(412.5F, geothermal.saveWithoutMetadata(NativeTestBootstrap.registries()).getFloat("Temperature"), 0.0F); + geothermal.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, NativeTestBootstrap.registries(), tag)); + assertEquals(412.5F, geothermal.saveWithoutMetadata(NativeTestBootstrap.registries()).getFloatOr("Temperature", 0.0F), 0.0F); tag.putFloat("Temperature", 500.0F); - geothermal.loadWithComponents(tag, NativeTestBootstrap.registries()); - assertEquals(500.0F, geothermal.saveWithoutMetadata(NativeTestBootstrap.registries()).getFloat("Temperature"), 0.0F); + geothermal.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, NativeTestBootstrap.registries(), tag)); + assertEquals(500.0F, geothermal.saveWithoutMetadata(NativeTestBootstrap.registries()).getFloatOr("Temperature", 0.0F), 0.0F); } @Test public void stillKeepsSeparateTanksAndExplicitlyEmptyReloads() { @@ -100,15 +108,18 @@ public class SteamMachinePersistenceTest { tag.put("TankIn", water(625)); tag.put("TankOut", water(375)); SteamStillTileEntity still = new SteamStillTileEntity(net.minecraft.core.BlockPos.ZERO, NativeMachineFixture.state("steam_still")); - still.loadWithComponents(tag, NativeTestBootstrap.registries()); + still.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, NativeTestBootstrap.registries(), tag)); SteamStillTileEntity restored = new SteamStillTileEntity(net.minecraft.core.BlockPos.ZERO, NativeMachineFixture.state("steam_still")); - restored.loadWithComponents(still.saveWithoutMetadata(NativeTestBootstrap.registries()), NativeTestBootstrap.registries()); + restored.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, NativeTestBootstrap.registries(), still.saveWithoutMetadata(NativeTestBootstrap.registries()))); assertEquals(625, restored.getInputTank().getFluidAmount()); assertEquals(375, restored.getOutputTank().getFluidAmount()); assertEquals(37.5F, restored.getEnergy(Power.steam_power), 0.0F); tag.put("TankIn", water(0)); tag.put("TankOut", water(0)); - restored.loadWithComponents(tag, NativeTestBootstrap.registries()); + restored.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, NativeTestBootstrap.registries(), tag)); assertEquals(0, restored.getInputTank().getFluidAmount()); assertEquals(0, restored.getOutputTank().getFluidAmount()); } diff --git a/src/test/java/zone/moddev/mc/basesteam/machines/SteamOperationalPumpSourceTest.java b/src/test/java/zone/moddev/mc/basesteam/machines/SteamOperationalPumpSourceTest.java index 08cd263..8652ea0 100644 --- a/src/test/java/zone/moddev/mc/basesteam/machines/SteamOperationalPumpSourceTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/machines/SteamOperationalPumpSourceTest.java @@ -54,7 +54,7 @@ public class SteamOperationalPumpSourceTest { } @Test public void unsupportedModSourceIsNeverReplacedOrCredited() { - Block host = new Block(Block.Properties.of()) { + Block host = new Block(properties("unsupported_source")) { @Override public FluidState getFluidState(BlockState state) { return Fluids.WATER.defaultFluidState(); } }; BlockState source = host.defaultBlockState(); @@ -129,10 +129,10 @@ private static MethodNode method(ClassNode node, String name) { private static final class RefusingSource extends Block implements BucketPickup { private int pickups; - private RefusingSource() { super(Block.Properties.of()); } + private RefusingSource() { super(SteamOperationalPumpSourceTest.properties("refusing_source")); } @Override public FluidState getFluidState(BlockState state) { return Fluids.WATER.defaultFluidState(); } @Override public net.minecraft.world.item.ItemStack pickupBlock( - net.minecraft.world.entity.player.Player player, LevelAccessor world, BlockPos pos, BlockState state) { + net.minecraft.world.entity.LivingEntity player, LevelAccessor world, BlockPos pos, BlockState state) { assertNull("Automated pickup has no acting player", player); pickups++; return net.minecraft.world.item.ItemStack.EMPTY; @@ -140,6 +140,12 @@ private static final class RefusingSource extends Block implements BucketPickup @Override public java.util.Optional getPickupSound() { return java.util.Optional.empty(); } } + private static Block.Properties properties(String name) { + return Block.Properties.of().setId(net.minecraft.resources.ResourceKey.create( + net.minecraft.core.registries.Registries.BLOCK, + net.minecraft.resources.Identifier.fromNamespaceAndPath("basesteam_test", name))); + } + private static final class PickupWorld { private BlockState state; private int writes; diff --git a/src/test/java/zone/moddev/mc/basesteam/machines/WaterBoilerTankNotificationTest.java b/src/test/java/zone/moddev/mc/basesteam/machines/WaterBoilerTankNotificationTest.java index c36ae17..ab4bdd3 100644 --- a/src/test/java/zone/moddev/mc/basesteam/machines/WaterBoilerTankNotificationTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/machines/WaterBoilerTankNotificationTest.java @@ -84,7 +84,7 @@ public class WaterBoilerTankNotificationTest { assertEquals(5, fill(water(5), true)); assertEquals(15, probe.tank().getFluidAmount()); assertEquals(0, probe.save().dirtyCalls); - assertEquals(10, probe.save().savedTank.getInt("Amount")); + assertEquals(10, probe.save().savedTank.getIntOr("Amount", 0)); } @Test public void aClientDirectDrainDoesNotRequestServerSaving() { @@ -92,7 +92,7 @@ public class WaterBoilerTankNotificationTest { assertEquals(5, probe.tank().drain(5, true).amount); assertEquals(5, probe.tank().getFluidAmount()); assertEquals(0, probe.save().dirtyCalls); - assertEquals(10, probe.save().savedTank.getInt("Amount")); + assertEquals(10, probe.save().savedTank.getIntOr("Amount", 0)); } private static FluidStack water(int amount) { return new FluidStack(net.minecraft.world.level.material.Fluids.WATER, amount); } @@ -100,12 +100,12 @@ public class WaterBoilerTankNotificationTest { private void saved(int amount) { assertTrue("Real server-side tank mutation must notify its owner immediately", probe.save().dirtyCalls > 0); assertEquals(amount, probe.tank().getFluidAmount()); - assertEquals(amount, probe.save().savedTank.getInt("Amount")); + assertEquals(amount, probe.save().savedTank.getIntOr("Amount", 0)); } private void unchanged(int amount) { assertEquals(0, probe.save().dirtyCalls); assertEquals(amount, probe.tank().getFluidAmount()); - assertEquals(amount, probe.save().savedTank.getInt("Amount")); + assertEquals(amount, probe.save().savedTank.getIntOr("Amount", 0)); } private int fill(FluidStack offer, boolean execute) throws Exception { return ((Number) probe.machine().getClass().getMethod("fill", net.minecraft.core.Direction.class, FluidStack.class, boolean.class) @@ -162,6 +162,6 @@ private static Level world(boolean client) { } catch (ReflectiveOperationException failure) { throw new AssertionError(failure); } } private static final class SaveWorld extends net.minecraft.server.level.ServerLevel { - private SaveWorld() { super(null, null, null, null, null, null, null, false, 0L, java.util.Collections.emptyList(), false, null); } + private SaveWorld() { super(null, null, null, null, null, null, false, 0L, java.util.Collections.emptyList(), false, null); } } } diff --git a/src/test/java/zone/moddev/mc/basesteam/migration/SteamLegacyMigrationsTest.java b/src/test/java/zone/moddev/mc/basesteam/migration/SteamLegacyMigrationsTest.java index 0a694cb..54ff55b 100644 --- a/src/test/java/zone/moddev/mc/basesteam/migration/SteamLegacyMigrationsTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/migration/SteamLegacyMigrationsTest.java @@ -7,7 +7,7 @@ import cyano.poweradvantage.api.migration.LegacyMigrationApi; import net.minecraft.nbt.CompoundTag; import net.minecraft.core.Direction; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import org.junit.Test; public class SteamLegacyMigrationsTest { @@ -30,20 +30,20 @@ public class SteamLegacyMigrationsTest { oldTank.putInt("Amount", 1250); pump.put("TankOut", oldTank); assertTrue(SteamLegacyMigrations.repairPersistenceKeys(pump)); - assertEquals(1250, pump.getCompound("Tank").getInt("Amount")); + assertEquals(1250, pump.getCompoundOrEmpty("Tank").getIntOr("Amount", 0)); CompoundTag boiler = new CompoundTag(); boiler.putString("id", "steamadvantage:steam_boiler_geothermal"); boiler.putFloat("Tempoerature", 412.5F); assertTrue(SteamLegacyMigrations.repairPersistenceKeys(boiler)); - assertEquals(412.5F, boiler.getFloat("Temperature"), 0.0F); + assertEquals(412.5F, boiler.getFloatOr("Temperature", 0.0F), 0.0F); CompoundTag canonical = new CompoundTag(); canonical.putString("id", "basesteam:steam_boiler_geothermal"); canonical.putFloat("Temperature", 500.0F); canonical.putFloat("Tempoerature", 100.0F); assertFalse(SteamLegacyMigrations.repairPersistenceKeys(canonical)); - assertEquals(500.0F, canonical.getFloat("Temperature"), 0.0F); + assertEquals(500.0F, canonical.getFloatOr("Temperature", 0.0F), 0.0F); } @Test public void aliasesLegacyTileIds() { @@ -65,15 +65,15 @@ public class SteamLegacyMigrationsTest { "steam_boiler_geothermal", "steam_tank", "steam_furnace", "steam_crusher", "steam_drill", "drillbit", "platform", "steam_elevator", "steam_switch", "pump_pipe_steam", "steam_still", "steam_pump", "steam_boiler_oil"}) { - ResourceLocation current = ResourceLocation.fromNamespaceAndPath("basesteam", path); - assertEquals(current, LegacyMigrationApi.blockTarget(ResourceLocation.fromNamespaceAndPath("steamadvantage", path))); + Identifier current = Identifier.fromNamespaceAndPath("basesteam", path); + assertEquals(current, LegacyMigrationApi.blockTarget(Identifier.fromNamespaceAndPath("steamadvantage", path))); assertTrue(path, LegacyMigrationApi.blockStateCodecs().containsKey(current)); } - ResourceLocation oldMusket = ResourceLocation.parse("steamadvantage:musket"); - assertEquals(ResourceLocation.parse("basesteam:musket"), LegacyMigrationApi.itemTarget(oldMusket)); + Identifier oldMusket = Identifier.parse("steamadvantage:musket"); + assertEquals(Identifier.parse("basesteam:musket"), LegacyMigrationApi.itemTarget(oldMusket)); assertTrue(LegacyMigrationApi.isDurableItem(oldMusket)); - assertEquals(ResourceLocation.parse("basesteam:rapid_reload"), - LegacyMigrationApi.registryTarget(ResourceLocation.parse("steamadvantage:rapid_reload"))); + assertEquals(Identifier.parse("basesteam:rapid_reload"), + LegacyMigrationApi.registryTarget(Identifier.parse("steamadvantage:rapid_reload"))); } @Test public void repairsOldKeysForBothNamespacesWithoutDependingOnRemapOrder() { @@ -84,8 +84,8 @@ public class SteamLegacyMigrationsTest { tank.putInt("Amount", 1250); pump.put("TankOut", tank); assertTrue(source, SteamLegacyMigrations.repairPersistenceKeys(pump)); - assertEquals(source, pump.getString("id")); - assertEquals(tank, pump.getCompound("Tank")); + assertEquals(source, pump.getStringOr("id", "")); + assertEquals(tank, pump.getCompoundOrEmpty("Tank")); assertFalse(SteamLegacyMigrations.repairPersistenceKeys(pump)); } } diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/BuildWorkflowContractTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/BuildWorkflowContractTest.java index d6d4354..dc47669 100644 --- a/src/test/java/zone/moddev/mc/basesteam/resources/BuildWorkflowContractTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/resources/BuildWorkflowContractTest.java @@ -12,12 +12,12 @@ public class BuildWorkflowContractTest { try (var reader = Files.newBufferedReader(Path.of("gradle.properties"))) { pins.load(reader); } - assertEquals("1.21.1", pins.getProperty("minecraft_version")); - assertEquals("52.1.0", pins.getProperty("forge_version")); + assertEquals("1.21.11", pins.getProperty("minecraft_version")); + assertEquals("61.1.0", pins.getProperty("forge_version")); assertEquals("21", pins.getProperty("java_version")); assertEquals("official", pins.getProperty("mapping_channel")); - assertEquals("1.21.1", pins.getProperty("mapping_version")); - assertEquals("2.4.14.121011", pins.getProperty("poweradvantage_version")); + assertEquals("1.21.11", pins.getProperty("mapping_version")); + assertEquals("2.4.15.121111", pins.getProperty("poweradvantage_version")); for (String key : new String[] {"poweradvantage_source_ref", "poweradvantage_deobf_sha256", "poweradvantage_player_sha256", "poweradvantage_test_bootstrap_sha256", "poweradvantage_test_bootstrap_source_sha256", "poweradvantage_test_registry_source_sha256", @@ -29,7 +29,7 @@ public class BuildWorkflowContractTest { } assertEquals(pins.getProperty("poweradvantage_deobf_sha256"), pins.getProperty("poweradvantage_player_sha256")); String build = Files.readString(Path.of("build.gradle")); - assertTrue(build.contains("apply from: 'gradle/forge52-mavenizer.gradle'")); + assertTrue(build.contains("apply from: 'gradle/forge61-mavenizer.gradle'")); assertFalse(build.contains("renamer.classes(")); assertFalse(build.contains("toSrgFile")); assertTrue(build.contains("basePower-${project.poweradvantage_version}.jar")); @@ -40,9 +40,9 @@ public class BuildWorkflowContractTest { String source = Files.readString(Path.of(".github/workflows", name)); int pinCheck = source.indexOf("bash .github/verify-published-pins.sh"); assertTrue(name, pinCheck > 0 && pinCheck < source.indexOf("Check out released basePower source")); - assertTrue(name, source.contains("branches: [master-1.21.1")); + assertTrue(name, source.contains("branches: [master-1.21.11")); assertTrue(name, source.contains("java-version: '21.0.7+6.0.LTS'")); - assertTrue(name, source.contains("-Pforge52MavenizerJar=$MAVENIZER")); + assertTrue(name, source.contains("-Pforge61MavenizerJar=$MAVENIZER")); assertTrue(name, source.contains("-PpowerAdvantageDeobfJar=$POWER_PLAYER")); assertFalse(name, source.contains("forge47")); } diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/MusketFragileTagContractTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/MusketFragileTagContractTest.java index 84131c1..96e8543 100644 --- a/src/test/java/zone/moddev/mc/basesteam/resources/MusketFragileTagContractTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/resources/MusketFragileTagContractTest.java @@ -8,7 +8,7 @@ import java.util.HexFormat; import java.util.TreeSet; import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import org.junit.BeforeClass; import org.junit.Test; import zone.moddev.mc.basesteam.testing.NativeTestBootstrap; @@ -31,7 +31,7 @@ public class MusketFragileTagContractTest { assertTrue(blocks.contains("minecraft:short_grass")); for (String id : blocks) { assertTrue("Unregistered Forge 52 block would invalidate the tag: " + id, - BuiltInRegistries.BLOCK.containsKey(ResourceLocation.parse(id))); + BuiltInRegistries.BLOCK.containsKey(Identifier.parse(id))); } assertFalse(blocks.contains("minecraft:tnt")); assertFalse(blocks.contains("minecraft:stone")); diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/NativeBlockEntityContractTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/NativeBlockEntityContractTest.java index 055f99c..06c39bf 100644 --- a/src/test/java/zone/moddev/mc/basesteam/resources/NativeBlockEntityContractTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/resources/NativeBlockEntityContractTest.java @@ -44,7 +44,8 @@ public class NativeBlockEntityContractTest { saved.putInt("x", 7); saved.putInt("y", 9); saved.putInt("z", 11); saved.putIntArray("Energy", new int[] { Float.floatToIntBits(37.5F) }); saved.putByte("Facing", (byte) direction.get3DDataValue()); - drill.loadWithComponents(saved, NativeTestBootstrap.registries()); + drill.loadWithComponents(net.minecraft.world.level.storage.TagValueInput.create( + net.minecraft.util.ProblemReporter.DISCARDING, NativeTestBootstrap.registries(), saved)); assertEquals(destination, drill.getBlockPos()); assertSame(state, drill.getBlockState()); assertEquals(direction, drill.getBlockState().getValue(SteamDrillBlock.FACING)); @@ -56,7 +57,7 @@ public class NativeBlockEntityContractTest { DrillBitBlock block = new DrillBitBlock(); BlockEntityType previous = Entities.DRILLBIT; try { - Entities.DRILLBIT = BlockEntityType.Builder.of(DrillBitTileEntity::new, block).build(null); + Entities.DRILLBIT = new BlockEntityType<>(DrillBitTileEntity::new, java.util.Set.of(block)); assertNotNull(block.getTicker(null, block.defaultBlockState(), Entities.DRILLBIT)); assertNull(block.getTicker(null, block.defaultBlockState(), BlockEntityType.FURNACE)); DrillBitTileEntity tile = new DrillBitTileEntity(new BlockPos(17, 80, 31), block.defaultBlockState()); @@ -69,8 +70,8 @@ public class NativeBlockEntityContractTest { @Test public void nativeConstructorStillRejectsAMismatchedTypeAndBlock() { BlockEntityType previous = Entities.DRILLBIT; try { - Entities.DRILLBIT = BlockEntityType.Builder.of(DrillBitTileEntity::new, - net.minecraft.world.level.block.Blocks.STONE).build(null); + Entities.DRILLBIT = new BlockEntityType<>(DrillBitTileEntity::new, + java.util.Set.of(net.minecraft.world.level.block.Blocks.STONE)); assertThrows(IllegalStateException.class, () -> new DrillBitTileEntity( BlockPos.ZERO, NativeMachineFixture.state("drillbit"))); } finally { Entities.DRILLBIT = previous; } diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/NativeBlockPropertiesTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/NativeBlockPropertiesTest.java index f2e18a1..fde65cb 100644 --- a/src/test/java/zone/moddev/mc/basesteam/resources/NativeBlockPropertiesTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/resources/NativeBlockPropertiesTest.java @@ -13,7 +13,6 @@ import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.world.level.material.PushReaction; -import net.minecraft.world.level.storage.loot.BuiltInLootTables; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; @@ -50,7 +49,7 @@ public static Collection cases() { @Test public void remainsNonOpaque() { for (BlockState state : states()) { - assertFalse(state.toString(), state.isSolidRender(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)); + assertFalse(state.toString(), state.isSolidRender()); } } @@ -58,7 +57,7 @@ public static Collection cases() { // Native opacity is 1, not 0, for a non-solid block with a full-cube outline. int expected = height < 0 || height == 4 ? 1 : 0; for (BlockState state : states()) { - assertEquals(state.toString(), expected, state.getLightBlock(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)); + assertEquals(state.toString(), expected, state.getLightBlock()); } } @@ -92,7 +91,7 @@ public static Collection cases() { state.canBeReplaced(net.minecraft.world.level.material.Fluids.WATER)); assertEquals(height < 0 ? PushReaction.BLOCK : PushReaction.NORMAL, state.getPistonPushReaction()); } - if (height >= 0) assertEquals(BuiltInLootTables.EMPTY, block.getLootTable()); + if (height >= 0) assertTrue("Hidden helpers have no native loot-table key", block.getLootTable().isEmpty()); } @Test public void fullCollisionHelpersNeitherSuffocateNorBlockTheView() { diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/NativeEnchantmentContractTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/NativeEnchantmentContractTest.java index d5ff346..c0d4806 100644 --- a/src/test/java/zone/moddev/mc/basesteam/resources/NativeEnchantmentContractTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/resources/NativeEnchantmentContractTest.java @@ -17,7 +17,7 @@ import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.tags.ItemTags; import net.minecraft.tags.TagKey; import net.minecraft.world.entity.EquipmentSlot; @@ -43,17 +43,18 @@ public class NativeEnchantmentContractTest { NativeTestBootstrap.start(); Items.init(); Items.allItems.forEach((id, item) -> { - ResourceLocation key = ResourceLocation.fromNamespaceAndPath("basesteam", id); + Identifier key = Identifier.fromNamespaceAndPath("basesteam", id); if (!ForgeRegistries.ITEMS.containsKey(key)) ForgeRegistries.ITEMS.register(key, item); assertSame(item, ForgeRegistries.ITEMS.getValue(key)); }); previousTags = new HashMap<>(); - BuiltInRegistries.ITEM.getTags().forEach(pair -> - previousTags.put(pair.getFirst(), pair.getSecond().stream().toList())); + BuiltInRegistries.ITEM.freeze(); + BuiltInRegistries.ITEM.getTags().forEach(tag -> + previousTags.put(tag.key(), tag.stream().toList())); var tags = new HashMap<>(previousTags); - tags.put(ItemTags.create(ResourceLocation.parse("basesteam:muskets")), + tags.put(ItemTags.create(Identifier.parse("basesteam:muskets")), List.of(Items.blackpowder_musket.builtInRegistryHolder())); - BuiltInRegistries.ITEM.bindTags(tags); + NativeTestBootstrap.bindItemTags(tags); var enchantments = new MappedRegistry<>(Registries.ENCHANTMENT, Lifecycle.stable()); registries = new RegistryAccess.ImmutableRegistryAccess(Stream.concat( NativeTestBootstrap.registries().registries(), @@ -61,7 +62,7 @@ public class NativeEnchantmentContractTest { var ops = registries.createSerializationContext(JsonOps.INSTANCE); for (var key : KEYS) { try (var reader = Files.newBufferedReader(Path.of( - "src/main/resources/data/basesteam/enchantment", key.location().getPath() + ".json"))) { + "src/main/resources/data/basesteam/enchantment", key.identifier().getPath() + ".json"))) { Enchantment value = Enchantment.DIRECT_CODEC.parse(ops, JsonParser.parseReader(reader)).getOrThrow(); enchantments.register(key, value, RegistrationInfo.BUILT_IN); } @@ -70,7 +71,7 @@ public class NativeEnchantmentContractTest { } @AfterClass public static void restoreTags() { - if (previousTags != null) BuiltInRegistries.ITEM.bindTags(previousTags); + if (previousTags != null) NativeTestBootstrap.bindItemTags(previousTags); } @Test public void nativeDefinitionsRetainEveryLegacyCostLevelWeightAndSlot() { @@ -105,7 +106,8 @@ public class NativeEnchantmentContractTest { zone.moddev.mc.basesteam.items.MusketItem.startLoad(stack); zone.moddev.mc.basesteam.items.MusketItem.finishLoad(stack); zone.moddev.mc.basesteam.items.MusketItem.unload(stack); - ItemStack restored = ItemStack.parse(registries, stack.save(registries)).orElseThrow(); + ItemStack restored = ItemStack.CODEC.parse(registries.createSerializationContext(net.minecraft.nbt.NbtOps.INSTANCE), + ItemStack.CODEC.encodeStart(registries.createSerializationContext(net.minecraft.nbt.NbtOps.INSTANCE), stack).getOrThrow()).getOrThrow(); assertEquals(original, restored.get(DataComponents.ENCHANTMENTS)); assertEquals(123, restored.getDamageValue()); for (var key : KEYS) @@ -116,11 +118,11 @@ public class NativeEnchantmentContractTest { var stack = new ItemStack(Items.blackpowder_musket); assertThrows(IllegalStateException.class, () -> Enchantments.level(Enchantments.recoil, stack, RegistryAccess.EMPTY)); - var missing = ResourceKey.create(Registries.ENCHANTMENT, ResourceLocation.parse("basesteam:missing")); + var missing = ResourceKey.create(Registries.ENCHANTMENT, Identifier.parse("basesteam:missing")); assertThrows(IllegalStateException.class, () -> Enchantments.level(missing, stack, registries)); } private static Holder.Reference holder(ResourceKey key) { - return registries.registryOrThrow(Registries.ENCHANTMENT).getHolderOrThrow(key); + return registries.lookupOrThrow(Registries.ENCHANTMENT).getOrThrow(key); } } diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/NativeFuelContractTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/NativeFuelContractTest.java index 3bf11b4..57001a3 100644 --- a/src/test/java/zone/moddev/mc/basesteam/resources/NativeFuelContractTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/resources/NativeFuelContractTest.java @@ -12,11 +12,10 @@ import net.minecraft.tags.TagKey; import net.minecraft.tags.TagLoader; import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.core.Holder; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; -import net.minecraftforge.common.ForgeHooks; import net.minecraftforge.registries.ForgeRegistries; import java.util.HashMap; import java.util.List; @@ -36,22 +35,22 @@ public class NativeFuelContractTest { try (MultiPackResourceManager resources = new MultiPackResourceManager( PackType.SERVER_DATA, List.of(ServerPacksSource.createVanillaPackSource()))) { TagLoader> reader = new TagLoader<>( - id -> BuiltInRegistries.ITEM.getHolder(ResourceKey.create(Registries.ITEM, id)), "tags/item"); + (id, required) -> BuiltInRegistries.ITEM.get(ResourceKey.create(Registries.ITEM, id)) + .map(holder -> (Holder)holder), "tags/item"); Map, List>> loaded = new HashMap<>(); - reader.loadAndBuild(resources).forEach((id, tag) -> loaded.put(ItemTags.create(id), List.copyOf(tag))); + reader.build(reader.load(resources)).forEach((id, tag) -> loaded.put(ItemTags.create(id), List.copyOf(tag))); assertTrue(loaded.containsKey(ItemTags.NON_FLAMMABLE_WOOD)); previousTags = new HashMap<>(); - BuiltInRegistries.ITEM.getTags().forEach(pair -> - previousTags.put(pair.getFirst(), pair.getSecond().stream().toList())); - BuiltInRegistries.ITEM.bindTags(loaded); + BuiltInRegistries.ITEM.freeze(); + BuiltInRegistries.ITEM.getTags().forEach(tag -> + previousTags.put(tag.key(), tag.stream().toList())); + NativeTestBootstrap.bindItemTags(loaded); } - ForgeHooks.updateBurns(); } @AfterClass public static void restoreNativeTags() { if (previousTags != null) { - BuiltInRegistries.ITEM.bindTags(previousTags); - ForgeHooks.updateBurns(); + NativeTestBootstrap.bindItemTags(previousTags); } } @@ -64,13 +63,16 @@ public class NativeFuelContractTest { } @Test public void retainsTheForgeItemBurnTimeOverride() { - Item item = new Item(new Item.Properties()) { + ((net.minecraft.core.MappedRegistry)BuiltInRegistries.ITEM).unfreeze(); + Item item = new Item(new Item.Properties().setId(ResourceKey.create(Registries.ITEM, + Identifier.fromNamespaceAndPath("basesteam_test", "fuel_override")))) { @Override public int getBurnTime(ItemStack stack, RecipeType recipeType) { assertNull(recipeType); return 237; } }; - ForgeRegistries.ITEMS.register(ResourceLocation.fromNamespaceAndPath("basesteam_test", "fuel_override"), item); + ForgeRegistries.ITEMS.register(Identifier.fromNamespaceAndPath("basesteam_test", "fuel_override"), item); + BuiltInRegistries.ITEM.freeze(); assertEquals(237, FurnaceFuelHelper.getBurnTime(new ItemStack(item))); } } diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/NativeIngredientCodecContractTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/NativeIngredientCodecContractTest.java new file mode 100644 index 0000000..83bfbec --- /dev/null +++ b/src/test/java/zone/moddev/mc/basesteam/resources/NativeIngredientCodecContractTest.java @@ -0,0 +1,92 @@ +package zone.moddev.mc.basesteam.resources; + +import com.google.gson.JsonParser; +import com.mojang.serialization.JsonOps; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.packs.PackType; +import net.minecraft.server.packs.repository.ServerPacksSource; +import net.minecraft.server.packs.resources.MultiPackResourceManager; +import net.minecraft.tags.ItemTags; +import net.minecraft.tags.TagKey; +import net.minecraft.tags.TagLoader; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.crafting.Ingredient; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import zone.moddev.mc.basesteam.testing.NativeTestBootstrap; +import static org.junit.Assert.*; + +public class NativeIngredientCodecContractTest { + private static Map, List>> previousTags; + + @BeforeClass public static void bootstrap() throws Exception { + NativeTestBootstrap.registerCraftingSerializers(); + try (var resources = new MultiPackResourceManager(PackType.SERVER_DATA, + List.of(ServerPacksSource.createVanillaPackSource()))) { + TagLoader> reader = new TagLoader<>((id, required) -> BuiltInRegistries.ITEM + .get(ResourceKey.create(Registries.ITEM, id)).map(holder -> (Holder) holder), "tags/item"); + Map, List>> loaded = new HashMap<>(); + reader.build(reader.load(resources)).forEach((id, values) -> loaded.put(ItemTags.create(id), List.copyOf(values))); + assertTrue("Load real vanilla tag data", loaded.containsKey(ItemTags.PLANKS)); + previousTags = new HashMap<>(); + BuiltInRegistries.ITEM.freeze(); + BuiltInRegistries.ITEM.getTags().forEach(tag -> previousTags.put(tag.key(), tag.stream().toList())); + NativeTestBootstrap.bindItemTags(loaded); + } + } + + @AfterClass public static void restoreTags() { + if (previousTags != null) NativeTestBootstrap.bindItemTags(previousTags); + } + + @Test public void nativeItemStringKeepsItsExactMembershipAndWireShape() { + Ingredient ingredient = decode("\"minecraft:string\""); + assertTrue(ingredient.test(new ItemStack(Items.STRING))); + assertFalse(ingredient.test(new ItemStack(Items.STONE))); + assertFalse(ingredient.test(ItemStack.EMPTY)); + assertEquals(JsonParser.parseString("\"minecraft:string\""), Ingredient.CODEC.encodeStart( + NativeTestBootstrap.registries().createSerializationContext(JsonOps.INSTANCE), ingredient).getOrThrow()); + } + + @Test public void nativeTagStringUsesTheActualVanillaTagAndNotAnyItem() { + Ingredient ingredient = decode("\"#minecraft:planks\""); + for (Item plank : List.of(Items.OAK_PLANKS, Items.SPRUCE_PLANKS, Items.CRIMSON_PLANKS)) + assertTrue(ingredient.test(new ItemStack(plank))); + assertFalse(ingredient.test(new ItemStack(Items.STICK))); + assertFalse(ingredient.test(new ItemStack(Items.STONE))); + assertFalse(ingredient.test(ItemStack.EMPTY)); + } + + @Test public void nativeStringArrayMeansAlternativesWithoutAddingOtherItems() { + Ingredient ingredient = decode("[\"minecraft:string\",\"minecraft:feather\"]"); + assertTrue(ingredient.test(new ItemStack(Items.STRING))); + assertTrue(ingredient.test(new ItemStack(Items.FEATHER))); + assertFalse(ingredient.test(new ItemStack(Items.STONE))); + assertFalse(ingredient.test(ItemStack.EMPTY)); + } + + @Test public void legacyObjectsAndMalformedOrEmptySelectorsAreRejectedByTheNativeCodec() { + for (String json : List.of("{\"item\":\"minecraft:string\"}", "{\"tag\":\"minecraft:planks\"}", + "[{\"item\":\"minecraft:string\"}]", "{}", "[]", "null", "1", "\"#\"", + "\"INVALID:SPACE ID\"", "\"missing_mod:unregistered_item\"")) { + var result = Ingredient.CODEC.parse(NativeTestBootstrap.registries().createSerializationContext(JsonOps.INSTANCE), + JsonParser.parseString(json)); + assertTrue(json + " must produce a native codec error", result.error().isPresent()); + assertTrue(json + " cannot leave an accepted partial ingredient", result.result().isEmpty()); + } + } + + private static Ingredient decode(String json) { + return Ingredient.CODEC.parse(NativeTestBootstrap.registries().createSerializationContext(JsonOps.INSTANCE), + JsonParser.parseString(json)).getOrThrow(); + } +} diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/NativeItemAtlasContractTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/NativeItemAtlasContractTest.java new file mode 100644 index 0000000..9e360f2 --- /dev/null +++ b/src/test/java/zone/moddev/mc/basesteam/resources/NativeItemAtlasContractTest.java @@ -0,0 +1,126 @@ +package zone.moddev.mc.basesteam.resources; + +import com.google.gson.JsonParser; +import com.mojang.serialization.Dynamic; +import com.mojang.serialization.JsonOps; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.MessageDigest; +import java.util.HashMap; +import java.util.HashSet; +import java.util.HexFormat; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; +import java.util.Set; +import net.minecraft.client.renderer.texture.atlas.SpriteSource; +import net.minecraft.client.renderer.texture.atlas.SpriteSourceList; +import net.minecraft.client.renderer.texture.atlas.SpriteSources; +import net.minecraft.data.AtlasIds; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.Identifier; +import net.minecraft.server.packs.FilePackResources; +import net.minecraft.server.packs.PackLocationInfo; +import net.minecraft.server.packs.PackType; +import net.minecraft.server.packs.PathPackResources; +import net.minecraft.server.packs.repository.PackSource; +import net.minecraft.server.packs.resources.MultiPackResourceManager; +import org.junit.BeforeClass; +import org.junit.Test; +import zone.moddev.mc.basesteam.testing.NativeTestBootstrap; +import static org.junit.Assert.*; + +public class NativeItemAtlasContractTest { + @BeforeClass public static void bootstrap() { + NativeTestBootstrap.start(); + SpriteSources.bootstrap(); + } + + @Test public void pinnedNativeItemAtlasFindsEveryLegacySteamModelTextureAcrossNamespaces() throws Exception { + Properties pins = new Properties(); + try (var input = Files.newInputStream(Path.of("gradle.properties"))) { pins.load(input); } + Path power = Path.of("../../PowerAdvantage/build/libs/basePower-" + + pins.getProperty("poweradvantage_version") + ".jar"); + assertEquals(pins.getProperty("poweradvantage_deobf_sha256"), HexFormat.of().withUpperCase() + .formatHex(MessageDigest.getInstance("SHA-256").digest(Files.readAllBytes(power)))); + var steam = new PathPackResources.PathResourcesSupplier(Path.of("src/main/resources")); + var parent = new FilePackResources.FileResourcesSupplier(power); + Identifier atlas = Identifier.withDefaultNamespace("atlases/items.json"); + assertEquals(Identifier.withDefaultNamespace("items"), AtlasIds.ITEMS); + try (var resources = new MultiPackResourceManager(PackType.CLIENT_RESOURCES, + List.of(parent.openPrimary(info("basepower")), steam.openPrimary(info("basesteam"))))) { + var definitions = resources.getResourceStack(atlas); + assertFalse("The native item atlas must be supplied by the exact Power candidate", definitions.isEmpty()); + Map found = new HashMap<>(); + SpriteSource.Output output = new SpriteSource.Output() { + @Override public void add(Identifier id, SpriteSource.DiscardableLoader loader) { + var replaced = found.put(id, loader); + if (replaced != null) replaced.discard(); + } + @Override public void removeAll(java.util.function.Predicate filter) { + found.entrySet().removeIf(entry -> { + if (!filter.test(entry.getKey())) return false; + entry.getValue().discard(); + return true; + }); + } + }; + for (var definition : definitions) { + try (var reader = definition.openAsReader()) { + var sources = SpriteSources.FILE_CODEC.parse( + new Dynamic<>(JsonOps.INSTANCE, JsonParser.parseReader(reader))).getOrThrow(); + sources.forEach(source -> source.run(resources, output)); + } + } + try { + Set expected = textures(); + assertFalse("Inspect the actual Steam model texture declarations", expected.isEmpty()); + assertTrue("The loaded musket retains its original barrel-tip material texture", + expected.contains(Identifier.parse("basesteam:materials/steel_dark_hole"))); + assertTrue("The shared Power textures must also be covered", expected.stream() + .anyMatch(id -> id.getNamespace().equals("basepower"))); + Set missing = new HashSet<>(expected); + missing.removeAll(found.keySet()); + assertTrue("Textures absent from the actual native item-atlas sources: " + missing, missing.isEmpty()); + assertFalse(found.containsKey(Identifier.parse("basesteam:missing_atlas_negative_control"))); + for (Identifier id : expected) { + var file = id.withPrefix("textures/").withSuffix(".png"); + try (var input = resources.getResourceOrThrow(file).open()) { + assertArrayEquals(id.toString(), new byte[] {(byte)137, 80, 78, 71, 13, 10, 26, 10}, input.readNBytes(8)); + } + } + } finally { + found.values().forEach(SpriteSource.DiscardableLoader::discard); + } + } + try (var resources = new MultiPackResourceManager(PackType.CLIENT_RESOURCES, + List.of(steam.openPrimary(info("basesteam"))))) { + assertEquals("Steam contributes only its drill sprite", 1, resources.getResourceStack(atlas).size()); + assertTrue("Shared materials cannot exist without the required Power pack", resources.getResource( + Identifier.parse("basepower:textures/materials/wood.png")).isEmpty()); + assertEquals("Without Power, the native loader offers only the drill sprite and missing-texture fallback", 2, + SpriteSourceList.load(resources, AtlasIds.ITEMS).list(resources).size()); + } + } + + private static Set textures() throws IOException { + Set result = new HashSet<>(); + try (var files = Files.walk(Path.of("src/main/resources/assets/basesteam/models"))) { + for (Path file : files.filter(path -> path.toString().endsWith(".json")).toList()) { + var model = JsonParser.parseString(Files.readString(file)).getAsJsonObject(); + if (!model.has("textures")) continue; + for (var texture : model.getAsJsonObject("textures").entrySet()) { + String id = texture.getValue().getAsString(); + if (!id.startsWith("#")) result.add(Identifier.parse(id)); + } + } + } + return result; + } + + private static PackLocationInfo info(String mod) { + return new PackLocationInfo("mod:" + mod, Component.literal(mod), PackSource.DEFAULT, Optional.empty()); + } +} diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/NativeItemDefinitionContractTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/NativeItemDefinitionContractTest.java new file mode 100644 index 0000000..9e91ad4 --- /dev/null +++ b/src/test/java/zone/moddev/mc/basesteam/resources/NativeItemDefinitionContractTest.java @@ -0,0 +1,65 @@ +package zone.moddev.mc.basesteam.resources; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.mojang.serialization.JsonOps; +import java.nio.file.Files; +import java.nio.file.Path; +import net.minecraft.client.renderer.item.ClientItem; +import net.minecraft.client.renderer.item.ItemModels; +import net.minecraft.client.renderer.item.properties.conditional.ComponentMatches; +import net.minecraft.client.renderer.item.properties.conditional.ConditionalItemModelProperties; +import net.minecraft.core.component.DataComponents; +import net.minecraft.world.item.ItemDisplayContext; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.component.CustomData; +import org.junit.BeforeClass; +import org.junit.Test; +import zone.moddev.mc.basesteam.testing.NativeTestBootstrap; +import static org.junit.Assert.*; + +public class NativeItemDefinitionContractTest { + private static final Path ROOT = Path.of("src/main/resources/assets/basesteam/items"); + + @BeforeClass public static void bootstrap() { + NativeTestBootstrap.start(); + ItemModels.bootstrap(); + ConditionalItemModelProperties.bootstrap(); + } + + @Test public void allTwentyOneDefinitionsDecodeWithTheNativeClientCodec() throws Exception { + try (var files = Files.list(ROOT)) { + var definitions = files.filter(path -> path.toString().endsWith(".json")).toList(); + assertEquals(21, definitions.size()); + for (Path path : definitions) { + ClientItem item = ClientItem.CODEC.parse(JsonOps.INSTANCE, + JsonParser.parseString(Files.readString(path))).getOrThrow(); + assertNotNull(path.toString(), item.model()); + } + } + } + + @Test public void loadedPredicateUsesTheOriginalByteAndNeverChangesCustomData() throws Exception { + JsonObject model = JsonParser.parseString(Files.readString(ROOT.resolve("musket.json"))) + .getAsJsonObject().getAsJsonObject("model"); + assertEquals("basesteam:item/musket_loaded", model.getAsJsonObject("on_true").get("model").getAsString()); + var loading = model.getAsJsonObject("on_false"); + assertEquals("minecraft:using_item", loading.get("property").getAsString()); + assertEquals("basesteam:item/musket_loading", loading.getAsJsonObject("on_true").get("model").getAsString()); + assertEquals("basesteam:item/musket_unloaded", loading.getAsJsonObject("on_false").get("model").getAsString()); + var predicate = ComponentMatches.MAP_CODEC.codec().parse(JsonOps.INSTANCE, model).getOrThrow(); + ItemStack stack = new ItemStack(Items.STICK); + assertFalse(predicate.get(stack, null, null, 0, ItemDisplayContext.GUI)); + for (byte state : new byte[] {0, 1, 2, -1, 127}) { + CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> { + tag.putByte("loaded", state); + tag.putString("owner", "preserved"); + }); + CustomData before = stack.get(DataComponents.CUSTOM_DATA); + assertEquals(state == 1, predicate.get(stack, null, null, 0, ItemDisplayContext.GUI)); + assertSame(before, stack.get(DataComponents.CUSTOM_DATA)); + assertEquals("preserved", before.copyTag().getStringOr("owner", "")); + } + } +} diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/NativeLootContractTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/NativeLootContractTest.java index a0cbb18..f3abf36 100644 --- a/src/test/java/zone/moddev/mc/basesteam/resources/NativeLootContractTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/resources/NativeLootContractTest.java @@ -39,12 +39,12 @@ public class NativeLootContractTest { } } - @Test public void packUsesNative1211ResourceAndDataFormats() throws Exception { + @Test public void packUsesNative12111ResourceAndDataFormats() throws Exception { JsonObject pack = read(ROOT.resolve("pack.mcmeta")).getAsJsonObject("pack"); - assertEquals(34, pack.get("pack_format").getAsInt()); - JsonObject supported = pack.getAsJsonObject("supported_formats"); - assertEquals(34, supported.get("min_inclusive").getAsInt()); - assertEquals(48, supported.get("max_inclusive").getAsInt()); + assertEquals("[94,1]", pack.getAsJsonArray("min_format").toString()); + assertEquals("[94,1]", pack.getAsJsonArray("max_format").toString()); + assertFalse(pack.has("pack_format")); + assertFalse(pack.has("supported_formats")); assertFalse(pack.has("forge:server_data_pack_format")); assertFalse(pack.has("forge:resource_pack_format")); assertFalse(pack.has("forge:data_pack_format")); diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/NativePackFormatContractTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/NativePackFormatContractTest.java new file mode 100644 index 0000000..5d6db52 --- /dev/null +++ b/src/test/java/zone/moddev/mc/basesteam/resources/NativePackFormatContractTest.java @@ -0,0 +1,53 @@ +package zone.moddev.mc.basesteam.resources; + +import com.google.gson.JsonParser; +import com.mojang.serialization.JsonOps; +import java.nio.file.Files; +import java.nio.file.Path; +import net.minecraft.SharedConstants; +import net.minecraft.server.packs.PackType; +import net.minecraft.server.packs.metadata.pack.PackFormat; +import net.minecraft.server.packs.metadata.pack.PackMetadataSection; +import org.junit.Test; +import zone.moddev.mc.basesteam.testing.NativePackResourceProof; +import static org.junit.Assert.*; + +public class NativePackFormatContractTest { + @org.junit.BeforeClass public static void bootstrap() { + zone.moddev.mc.basesteam.testing.NativeTestBootstrap.start(); + } + + @Test public void nativeClientAndServerMetadataAcceptTheSameCombinedPack() throws Exception { + var proof = NativePackResourceProof.read(); + assertEquals(0, proof.getAsJsonArray("warningsAndErrors").size()); + var clientProof = proof.getAsJsonObject("CLIENT_RESOURCES"); + var serverProof = proof.getAsJsonObject("SERVER_DATA"); + assertEquals("mod_resources", clientProof.get("id").getAsString()); + assertEquals("net.minecraftforge.resource.DelegatingPackResources", clientProof.get("resourcesClass").getAsString()); + assertEquals("mod:basesteam", serverProof.get("id").getAsString()); + assertEquals("net.minecraft.server.packs.PathPackResources", serverProof.get("resourcesClass").getAsString()); + assertEquals("COMPATIBLE", clientProof.get("currentCompatibility").getAsString()); + assertEquals("COMPATIBLE", serverProof.get("currentCompatibility").getAsString()); + assertTrue(clientProof.get("resourcesVerified").getAsInt() > 0); + assertTrue(serverProof.get("resourcesVerified").getAsInt() > 0); + var pack = JsonParser.parseString(Files.readString(Path.of("src/main/resources/pack.mcmeta"))) + .getAsJsonObject().getAsJsonObject("pack"); + assertFalse(pack.has("pack_format")); + assertFalse(pack.has("supported_formats")); + var client = PackMetadataSection.forPackType(PackType.CLIENT_RESOURCES).codec() + .parse(JsonOps.INSTANCE, clientProof.get("nativeMetadata")).getOrThrow(); + var server = PackMetadataSection.forPackType(PackType.SERVER_DATA).codec() + .parse(JsonOps.INSTANCE, serverProof.get("nativeMetadata")).getOrThrow(); + assertEquals(server, PackMetadataSection.forPackType(PackType.SERVER_DATA).codec() + .parse(JsonOps.INSTANCE, pack).getOrThrow()); + var version = SharedConstants.getCurrentVersion(); + assertEquals(PackFormat.of(75, 0), version.packVersion(PackType.CLIENT_RESOURCES)); + assertEquals(PackFormat.of(94, 1), version.packVersion(PackType.SERVER_DATA)); + assertTrue(client.supportedFormats().isValueInRange(version.packVersion(PackType.CLIENT_RESOURCES))); + assertTrue(server.supportedFormats().isValueInRange(version.packVersion(PackType.SERVER_DATA))); + assertFalse(client.supportedFormats().isValueInRange(PackFormat.of(74))); + assertFalse(server.supportedFormats().isValueInRange(PackFormat.of(95))); + assertFalse(client.supportedFormats().isValueInRange(PackFormat.of(94, 1))); + assertFalse(server.supportedFormats().isValueInRange(PackFormat.of(75, 0))); + } +} diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/NativePackRangeContractTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/NativePackRangeContractTest.java new file mode 100644 index 0000000..3440153 --- /dev/null +++ b/src/test/java/zone/moddev/mc/basesteam/resources/NativePackRangeContractTest.java @@ -0,0 +1,56 @@ +package zone.moddev.mc.basesteam.resources; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.mojang.serialization.JsonOps; +import java.nio.file.Files; +import java.nio.file.Path; +import net.minecraft.server.packs.PackType; +import net.minecraft.server.packs.metadata.pack.PackFormat; +import org.junit.Test; +import zone.moddev.mc.basesteam.testing.NativePackResourceProof; +import static org.junit.Assert.*; + +public class NativePackRangeContractTest { + @Test public void combinedPackRangeAcceptsBothNativePackTypes() throws Exception { + var proof = NativePackResourceProof.read(); + assertEquals(0, proof.getAsJsonArray("warningsAndErrors").size()); + for (PackType type : PackType.values()) { + var side = proof.getAsJsonObject(type.name()); + JsonObject pack = side.getAsJsonObject("nativeMetadata"); + var range = PackFormat.packCodec(type).codec().parse(JsonOps.INSTANCE, pack).getOrThrow(); + assertEquals(type.toString(), type == PackType.CLIENT_RESOURCES, + range.isValueInRange(PackFormat.of(75, 0))); + assertEquals(type.toString(), type == PackType.SERVER_DATA, + range.isValueInRange(PackFormat.of(94, 1))); + assertFalse(range.isValueInRange(PackFormat.of(74))); + assertFalse(range.isValueInRange(PackFormat.of(95))); + } + } + + @Test public void omissionDoesNotSynthesizeMetadataInTheNativeModPathSupplier() throws Exception { + var root = Path.of("build/native-pack-resources").toAbsolutePath(); + Files.createDirectories(root); + var omitted = Files.createTempDirectory(root, "missing-metadata-"); + var info = new net.minecraft.server.packs.PackLocationInfo("mod:basesteam", + net.minecraft.network.chat.Component.literal("baseSteam"), + net.minecraft.server.packs.repository.PackSource.DEFAULT, java.util.Optional.empty()); + var supplier = new net.minecraft.server.packs.PathPackResources.PathResourcesSupplier(omitted); + try (var resources = supplier.openPrimary(info)) { + assertNull(resources.getRootResource("pack.mcmeta")); + for (PackType type : PackType.values()) { + assertNull(type.toString(), resources.getMetadataSection( + net.minecraft.server.packs.metadata.pack.PackMetadataSection.forPackType(type))); + } + } + } + + @Test public void dataOnlyRangeDoesNotQualifyClientResources() { + JsonObject dataOnly = JsonParser.parseString("{\"min_format\":[94,1],\"max_format\":[94,1]}") + .getAsJsonObject(); + var range = PackFormat.packCodec(PackType.CLIENT_RESOURCES).codec() + .parse(JsonOps.INSTANCE, dataOnly).getOrThrow(); + assertFalse(range.isValueInRange(PackFormat.of(75, 0))); + assertTrue(range.isValueInRange(PackFormat.of(94, 1))); + } +} diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/OptionalCrushingRecipeContractTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/OptionalCrushingRecipeContractTest.java index 48dc5fb..124a65a 100644 --- a/src/test/java/zone/moddev/mc/basesteam/resources/OptionalCrushingRecipeContractTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/resources/OptionalCrushingRecipeContractTest.java @@ -91,7 +91,7 @@ private static boolean baseGuard(JsonElement value) { assertFalse(condition, baseGuard(json(condition))); } - @Test public void nativeForge52CodecRetainsTheExactProviderIdentity() { + @Test public void nativeForge61CodecRetainsTheExactProviderIdentity() { JsonObject guard = json("{\"type\":\"forge:mod_loaded\",\"modid\":\"basemetals\"}"); ModLoadedCondition condition = ModLoadedCondition.CODEC.codec().parse(JsonOps.INSTANCE, guard).getOrThrow(); assertSame(ModLoadedCondition.CODEC, condition.codec()); @@ -106,9 +106,8 @@ private static boolean baseGuard(JsonElement value) { for (String machine : MACHINES) { JsonObject recipe = recipes.get("crushing_" + machine + ".json"); assertEquals("Only the original payload plus conditions is expected", 4, recipe.size()); - JsonObject ingredient = recipe.getAsJsonObject("ingredient"); - assertEquals(1, ingredient.size()); - assertEquals("basesteam:" + machine, ingredient.get("item").getAsString()); + assertTrue(recipe.get("ingredient").isJsonPrimitive()); + assertEquals("basesteam:" + machine, recipe.get("ingredient").getAsString()); JsonObject output = recipe.getAsJsonObject("result"); assertEquals(machine.equals("steam_tank") ? 1 : 2, output.size()); assertEquals(machine.equals("steam_governor") ? "basepower:sprocket" diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/RecipeRegistrationThreadTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/RecipeRegistrationThreadTest.java index d7b0cd9..7a80ce3 100644 --- a/src/test/java/zone/moddev/mc/basesteam/resources/RecipeRegistrationThreadTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/resources/RecipeRegistrationThreadTest.java @@ -17,7 +17,7 @@ public class RecipeRegistrationThreadTest { assertEquals(1L, mod.methods.stream().filter(method -> method.name.equals("")) .flatMap(RecipeRegistrationThreadTest::calls) .filter(call -> call.owner.equals(CONDITIONS) && call.name.equals("register") - && call.desc.equals("(Lnet/minecraftforge/eventbus/api/IEventBus;)V")) + && call.desc.equals("(Lnet/minecraftforge/eventbus/api/bus/BusGroup;)V")) .count()); assertFalse(mod.methods.stream().filter(method -> method.name.equals("commonSetup")) .flatMap(RecipeRegistrationThreadTest::calls).anyMatch(call -> call.owner.equals(CONDITIONS))); diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/RecipeUnlockContractTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/RecipeUnlockContractTest.java index d098e14..7781808 100644 --- a/src/test/java/zone/moddev/mc/basesteam/resources/RecipeUnlockContractTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/resources/RecipeUnlockContractTest.java @@ -19,7 +19,7 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; -import net.minecraft.advancements.critereon.ItemPredicate; +import net.minecraft.advancements.criterion.ItemPredicate; import net.minecraft.world.item.Items; import org.junit.BeforeClass; import org.junit.Test; @@ -65,14 +65,14 @@ public class RecipeUnlockContractTest { assertEquals("Only an item or tag predicate is expected", 1, predicate.size()); actual.add(predicateKey(predicate)); } - List choices = new ArrayList<>(); + List choices = new ArrayList<>(); if (recipe.has("key")) { for (Map.Entry entry : recipe.getAsJsonObject("key").entrySet()) collect(entry.getValue(), choices); } else collect(recipe.has("ingredients") ? recipe.get("ingredients") : recipe.get("ingredient"), choices); Set expected = choices.stream().map(RecipeUnlockContractTest::ingredientKey) .filter(key -> !IGNORED.contains(key)).collect(Collectors.toCollection(LinkedHashSet::new)); - if (expected.isEmpty()) for (JsonObject choice : choices) expected.add(ingredientKey(choice)); + if (expected.isEmpty()) for (JsonElement choice : choices) expected.add(ingredientKey(choice)); assertEquals("Trigger must correspond to a relevant recipe ingredient: " + file, expected, actual); JsonArray requirements = unlock.getAsJsonArray("requirements"); assertEquals("All criteria must form one OR group", 1, requirements.size()); @@ -110,16 +110,16 @@ public class RecipeUnlockContractTest { } @Test public void nativeSingletonPredicatesKeepRecipeIngredientSemantics() { - JsonObject ingredient = new JsonParser().parse("{\"item\":\"minecraft:string\"}").getAsJsonObject(); + JsonElement ingredient = new com.google.gson.JsonPrimitive("minecraft:string"); JsonObject predicate = new JsonParser().parse("{\"items\":[\"minecraft:string\"]}").getAsJsonObject(); assertEquals("item:minecraft:string", predicateKey(predicate)); assertEquals(ingredientKey(ingredient), predicateKey(predicate)); - JsonObject tag = new JsonParser().parse("{\"tag\":\"minecraft:planks\"}").getAsJsonObject(); + JsonElement tag = new com.google.gson.JsonPrimitive("#minecraft:planks"); JsonObject nativeTag = new JsonParser().parse("{\"items\":\"#minecraft:planks\"}").getAsJsonObject(); assertEquals("tag:minecraft:planks", predicateKey(nativeTag)); assertEquals(ingredientKey(tag), predicateKey(nativeTag)); assertThrows("Legacy item predicates must not compare equal to native predicates", - AssertionError.class, () -> predicateKey(ingredient)); + AssertionError.class, () -> predicateKey(new JsonParser().parse("{\"item\":\"minecraft:string\"}").getAsJsonObject())); } @Test public void malformedOrBroadItemPredicatesAreRejected() { @@ -188,18 +188,16 @@ private static String predicateKey(JsonObject predicate) { return "item:" + item.getAsString(); } - private static String ingredientKey(JsonObject ingredient) { - assertEquals("Only one recipe item or tag ingredient is expected", 1, ingredient.size()); - String type = ingredient.has("tag") ? "tag" : "item"; - assertTrue("Recipe ingredients retain singular item or tag syntax", ingredient.has(type)); - JsonElement value = ingredient.get(type); - assertTrue("Recipe ingredient must contain a string ID", value.isJsonPrimitive() && value.getAsJsonPrimitive().isString()); - return type + ":" + value.getAsString(); + private static String ingredientKey(JsonElement ingredient) { + assertTrue("Native recipe ingredient must contain a string ID", + ingredient.isJsonPrimitive() && ingredient.getAsJsonPrimitive().isString()); + String value = ingredient.getAsString(); + return value.startsWith("#") ? "tag:" + value.substring(1) : "item:" + value; } - private static void collect(JsonElement element, List result) { + private static void collect(JsonElement element, List result) { if (element.isJsonArray()) for (JsonElement child : element.getAsJsonArray()) collect(child, result); - else result.add(element.getAsJsonObject()); + else result.add(element); } private static JsonObject read(Path file) throws IOException { diff --git a/src/test/java/zone/moddev/mc/basesteam/testing/EclipseUnitLauncher.java b/src/test/java/zone/moddev/mc/basesteam/testing/EclipseUnitLauncher.java new file mode 100644 index 0000000..8f3490a --- /dev/null +++ b/src/test/java/zone/moddev/mc/basesteam/testing/EclipseUnitLauncher.java @@ -0,0 +1,43 @@ +package zone.moddev.mc.basesteam.testing; + +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.runner.JUnitCore; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunListener; + +/** Emits exact JUnit counts for the separately compiled Eclipse test scope. */ +public final class EclipseUnitLauncher { + private EclipseUnitLauncher() {} + + public static void main(String[] arguments) throws Exception { + if (arguments.length < 2) throw new IllegalArgumentException("Receipt path and test classes required"); + Class[] tests = new Class[arguments.length - 1]; + for (int index = 1; index < arguments.length; index++) tests[index - 1] = Class.forName(arguments[index]); + AtomicInteger assumptions = new AtomicInteger(); + JUnitCore core = new JUnitCore(); + core.addListener(new RunListener() { + @Override public void testAssumptionFailure(Failure failure) { assumptions.incrementAndGet(); } + @Override public void testFailure(Failure failure) { System.err.println(failure.getTrace()); } + }); + var result = core.run(tests); + JsonObject receipt = new JsonObject(); + receipt.add("classes", new com.google.gson.Gson().toJsonTree(Arrays.copyOfRange(arguments, 1, arguments.length))); + receipt.addProperty("tests", result.getRunCount()); + receipt.addProperty("failures", result.getFailureCount()); + receipt.addProperty("ignored", result.getIgnoreCount()); + receipt.addProperty("assumptions", assumptions.get()); + receipt.addProperty("runtimeMilliseconds", result.getRunTime()); + boolean passed = result.wasSuccessful() && result.getRunCount() > 0 + && result.getIgnoreCount() == 0 && assumptions.get() == 0; + receipt.addProperty("passed", passed); + Files.writeString(Path.of(arguments[0]), new GsonBuilder().setPrettyPrinting().create().toJson(receipt) + "\n"); + System.out.println("ECJ_JUNIT tests=" + result.getRunCount() + " failures=" + result.getFailureCount() + + " ignored=" + result.getIgnoreCount() + " assumptions=" + assumptions.get()); + if (!passed) System.exit(1); + } +} diff --git a/src/test/java/zone/moddev/mc/basesteam/testing/NativeMachineFixture.java b/src/test/java/zone/moddev/mc/basesteam/testing/NativeMachineFixture.java index 64843b3..4690e2d 100644 --- a/src/test/java/zone/moddev/mc/basesteam/testing/NativeMachineFixture.java +++ b/src/test/java/zone/moddev/mc/basesteam/testing/NativeMachineFixture.java @@ -5,7 +5,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; import net.minecraft.core.BlockPos; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; @@ -98,12 +98,12 @@ private Fixture(String id, Block block, BlockEntityType.BlockEntitySupplier f this.block = block; this.getter = getter; this.setter = setter; - type = BlockEntityType.Builder.of(factory, block).build(null); + type = new BlockEntityType<>(factory, java.util.Set.of(block)); ForgeRegistry> registry = (ForgeRegistry>) ForgeRegistries.BLOCK_ENTITY_TYPES; boolean locked = registry.isLocked(); registry.unfreeze(); try { - registry.register(ResourceLocation.fromNamespaceAndPath("basesteam", id), type); + registry.register(Identifier.fromNamespaceAndPath("basesteam", id), type); } finally { if (locked) registry.freeze(); } diff --git a/src/test/java/zone/moddev/mc/basesteam/testing/NativePackResourceProof.java b/src/test/java/zone/moddev/mc/basesteam/testing/NativePackResourceProof.java new file mode 100644 index 0000000..1293c69 --- /dev/null +++ b/src/test/java/zone/moddev/mc/basesteam/testing/NativePackResourceProof.java @@ -0,0 +1,185 @@ +package zone.moddev.mc.basesteam.testing; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import cpw.mods.jarhandling.SecureJar; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import net.minecraft.SharedConstants; +import net.minecraft.server.Bootstrap; +import net.minecraft.server.packs.PackType; +import net.minecraft.server.packs.PathPackResources; +import net.minecraft.server.packs.metadata.pack.PackMetadataSection; +import net.minecraft.server.packs.repository.Pack; +import net.minecraft.server.packs.repository.PackRepository; +import net.minecraft.server.packs.resources.MultiPackResourceManager; +import net.minecraft.resources.Identifier; +import net.minecraftforge.fml.ModList; +import net.minecraftforge.fml.loading.moddiscovery.ModFile; +import net.minecraftforge.fml.loading.moddiscovery.ModFileParser; +import net.minecraftforge.fml.loading.moddiscovery.ModInfo; +import net.minecraftforge.fml.loading.targets.ForgeDevLocator; +import net.minecraftforge.resource.ResourcePackLoader; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.config.Property; +import org.apache.logging.log4j.core.layout.PatternLayout; + +/** Runs real Forge discovery in a child VM so ModList and native registry state cannot leak into other tests. */ +public final class NativePackResourceProof { + private static JsonObject receipt; + + private NativePackResourceProof() {} + + public static synchronized JsonObject read() throws Exception { + if (receipt != null) return receipt.deepCopy(); + Path evidenceRoot = Path.of("build/native-pack-resources").toAbsolutePath(); + Files.createDirectories(evidenceRoot); + Path evidence = Files.createTempDirectory(evidenceRoot, "forge61-"); + String classpath = Arrays.stream(System.getProperty("basesteam.nativePackClasspath", + System.getProperty("java.class.path")).split(java.util.regex.Pattern.quote(java.io.File.pathSeparator))) + .map(entry -> new java.io.File(entry).getAbsolutePath()) + .collect(java.util.stream.Collectors.joining(java.io.File.pathSeparator)); + Path javaExecutable = Path.of(System.getProperty("java.home"), "bin", "java"); + if (!Files.isExecutable(javaExecutable)) javaExecutable = javaExecutable.resolveSibling("java.exe"); + Path log = evidence.resolve("forge-resources.log"); + Process process = new ProcessBuilder(javaExecutable.toString(), "-XX:ActiveProcessorCount=4", "-Xmx512m", + "--add-opens", "java.base/java.lang.invoke=ALL-UNNAMED", "-cp", classpath, + NativePackResourceProof.class.getName(), Path.of("build/resources/main").toAbsolutePath().toString(), + evidence.toString()).directory(evidence.toFile()) + .redirectErrorStream(true).redirectOutput(log.toFile()).start(); + if (!process.waitFor(90, TimeUnit.SECONDS)) { + process.destroyForcibly().waitFor(); + throw new AssertionError("Native Forge resource proof timed out: " + log); + } + String output = Files.readString(log); + if (process.exitValue() != 0) throw new AssertionError("Native Forge resource proof failed: " + log + "\n" + output); + if (java.util.regex.Pattern.compile("(?m)(?:\\[[^\\r\\n]*\\b(?:WARN|ERROR|FATAL)\\b[^\\r\\n]*\\]|\\bmain (?:WARN|ERROR|FATAL)\\b)") + .matcher(output).find()) { + throw new AssertionError("Native Forge resource diagnostic in " + log + "\n" + output); + } + receipt = JsonParser.parseString(Files.readString(evidence.resolve("receipt.json"))).getAsJsonObject(); + return receipt.deepCopy(); + } + + public static void main(String[] args) throws Exception { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + Path resources = Path.of(args[0]); + Path evidence = Path.of(args[1]); + Path jar = evidence.resolve("basesteam-resource-contract.jar"); + Manifest manifest = new Manifest(); + manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); + manifest.getMainAttributes().putValue("Automatic-Module-Name", "basesteam"); + try (JarOutputStream output = new JarOutputStream(Files.newOutputStream(jar), manifest); + var files = Files.walk(resources)) { + for (Path file : files.filter(Files::isRegularFile).sorted().toList()) { + output.putNextEntry(new JarEntry(resources.relativize(file).toString().replace('\\', '/'))); + Files.copy(file, output); + output.closeEntry(); + } + } + ModFile mod = new ModFile(SecureJar.from(jar), new ForgeDevLocator(), ModFileParser::modsTomlParser); + if (mod.getModFileInfo().showAsResourcePack()) throw new AssertionError("Steam must use Forge's hidden client pack"); + ModList.of(List.of(mod), mod.getModInfos().stream().map(ModInfo.class::cast).toList()); + List diagnostics = new ArrayList<>(); + AbstractAppender appender = new AbstractAppender("SteamPackContract", null, + PatternLayout.createDefaultLayout(), false, Property.EMPTY_ARRAY) { + @Override public void append(LogEvent event) { + if (event.getLevel().isMoreSpecificThan(Level.WARN)) diagnostics.add(event.getMessage().getFormattedMessage()); + } + }; + appender.start(); + var logger = (org.apache.logging.log4j.core.Logger)LogManager.getRootLogger(); + logger.addAppender(appender); + JsonObject result = new JsonObject(); + try { + for (PackType type : PackType.values()) { + PackRepository repository = new PackRepository(); + ResourcePackLoader.loadResourcePacks(repository, type == PackType.CLIENT_RESOURCES); + repository.reload(); + String id = type == PackType.CLIENT_RESOURCES ? "mod_resources" : "mod:basesteam"; + if (repository.getAvailableIds().size() != 1 || !repository.isAvailable(id)) { + throw new AssertionError("Unexpected native packs: " + repository.getAvailableIds()); + } + Pack pack = repository.getPack(id); + if (pack == null) throw new AssertionError("Forge did not discover " + id); + JsonObject side = new JsonObject(); + side.addProperty("id", id); + side.addProperty("loaderCompatibility", pack.getCompatibility().name()); + try (var opened = pack.open()) { + PackMetadataSection metadata = opened.getMetadataSection(PackMetadataSection.forPackType(type)); + if (metadata == null) throw new AssertionError("Missing native " + type + " metadata"); + var formats = metadata.supportedFormats(); + var codec = PackMetadataSection.forPackType(type).codec(); + var encoded = codec.encodeStart(com.mojang.serialization.JsonOps.INSTANCE, metadata).getOrThrow(); + var decoded = codec.parse(com.mojang.serialization.JsonOps.INSTANCE, encoded).getOrThrow(); + // Native JSON widens numeric translation arguments; compare the wire value and exact range. + if (!formats.equals(decoded.supportedFormats()) + || !encoded.equals(codec.encodeStart(com.mojang.serialization.JsonOps.INSTANCE, decoded).getOrThrow())) { + throw new AssertionError("Native metadata round-trip changed " + type); + } + side.addProperty("resourcesClass", opened.getClass().getName()); + side.addProperty("acceptsClient", formats.isValueInRange(SharedConstants.getCurrentVersion().packVersion(PackType.CLIENT_RESOURCES))); + side.addProperty("acceptsServer", formats.isValueInRange(SharedConstants.getCurrentVersion().packVersion(PackType.SERVER_DATA))); + side.addProperty("accepts74", formats.isValueInRange(net.minecraft.server.packs.metadata.pack.PackFormat.of(74))); + side.addProperty("accepts95", formats.isValueInRange(net.minecraft.server.packs.metadata.pack.PackFormat.of(95))); + side.add("nativeMetadata", encoded); + if (type == PackType.CLIENT_RESOURCES) { + if (opened.getChildren() == null || opened.getChildren().size() != 1) { + throw new AssertionError("Expected the actual Forge client delegate"); + } + side.addProperty("currentCompatibility", pack.getCompatibility().name()); + } else { + var supplier = new PathPackResources.PathResourcesSupplier(mod.findResource("")); + var current = Pack.readPackMetadata(pack.location(), supplier, + SharedConstants.getCurrentVersion().packVersion(type), type); + if (current == null) throw new AssertionError("Native current-version server metadata was rejected"); + side.addProperty("currentCompatibility", current.compatibility().name()); + } + } + repository.setSelected(List.of(id)); + int checked = 0; + try (MultiPackResourceManager manager = new MultiPackResourceManager(type, repository.openAllSelected()); + var files = Files.walk(resources.resolve(type.getDirectory()))) { + for (Path file : files.filter(Files::isRegularFile).sorted().toList()) { + Path relative = resources.resolve(type.getDirectory()).relativize(file); + String namespace = relative.getName(0).toString(); + String path = relative.subpath(1, relative.getNameCount()).toString().replace('\\', '/'); + var resource = manager.getResource(Identifier.fromNamespaceAndPath(namespace, path)).orElseThrow(); + try (var input = resource.open()) { + if (!Arrays.equals(Files.readAllBytes(file), input.readAllBytes())) { + throw new AssertionError("Forge changed " + type + " resource " + relative); + } + } + checked++; + } + } + if (checked == 0) throw new AssertionError("No native " + type + " resources were read"); + side.addProperty("resourcesVerified", checked); + result.add(type.name(), side); + } + } finally { + logger.removeAppender(appender); + appender.stop(); + } + JsonArray messages = new JsonArray(); + diagnostics.forEach(messages::add); + result.add("warningsAndErrors", messages); + Files.writeString(evidence.resolve("receipt.json"), result.toString(), StandardCharsets.UTF_8); + if (!diagnostics.isEmpty()) throw new AssertionError("Native Forge resource diagnostics: " + diagnostics); + System.out.println("BASESTEAM_NATIVE_PACK_RESOURCES_PASS " + result); + } +} diff --git a/src/test/java/zone/moddev/mc/basesteam/testing/NativeTestBootstrap.java b/src/test/java/zone/moddev/mc/basesteam/testing/NativeTestBootstrap.java index 0af43f8..e614176 100644 --- a/src/test/java/zone/moddev/mc/basesteam/testing/NativeTestBootstrap.java +++ b/src/test/java/zone/moddev/mc/basesteam/testing/NativeTestBootstrap.java @@ -8,7 +8,6 @@ import net.minecraft.SharedConstants; import net.minecraft.server.Bootstrap; import net.minecraftforge.common.ForgeMod; -import net.minecraftforge.eventbus.api.Event; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.ForgeRegistry; @@ -35,17 +34,7 @@ public static synchronized void registerVanillaFluidTypes() throws ReflectiveOpe // Plain JUnit has no mod loader to dispatch these two Forge registry phases. if (ForgeRegistries.FLUID_TYPES.get() == null) { - var registries = ForgeRegistries.class.getDeclaredField("registries"); - registries.setAccessible(true); - DeferredRegister fluidTypes = ((List) registries.get(null)).stream() - .map(DeferredRegister.class::cast) - .filter(registry -> ForgeRegistries.Keys.FLUID_TYPES.equals(registry.getRegistryKey())) - .findFirst().orElseThrow(); - var event = new NewRegistryEvent(); - invokeRegistryPhase(fluidTypes, "createRegistry", event); - var fill = NewRegistryEvent.class.getDeclaredMethod("fill"); - fill.setAccessible(true); - fill.invoke(event); + createForgeRegistry(ForgeRegistries.Keys.FLUID_TYPES); } var vanillaTypes = ForgeMod.class.getDeclaredField("VANILLA_FLUID_TYPES"); @@ -56,7 +45,34 @@ public static synchronized void registerVanillaFluidTypes() throws ReflectiveOpe event.newInstance(ForgeRegistries.Keys.FLUID_TYPES, ForgeRegistries.FLUID_TYPES.get(), null)); } - private static void invokeRegistryPhase(DeferredRegister register, String phase, Event event) + public static synchronized void registerCraftingSerializers() throws ReflectiveOperationException { + start(); + if (ForgeRegistries.INGREDIENT_SERIALIZERS.get() == null) + createForgeRegistry(ForgeRegistries.Keys.INGREDIENT_SERIALIZERS); + var field = ForgeMod.class.getDeclaredField("INGREDIENT_SERIALIZERS"); + field.setAccessible(true); + var register = (DeferredRegister) field.get(null); + if (register.getEntries().stream().allMatch(net.minecraftforge.registries.RegistryObject::isPresent)) return; + var event = RegisterEvent.class.getDeclaredConstructor(ResourceKey.class, ForgeRegistry.class, Registry.class); + event.setAccessible(true); + invokeRegistryPhase(register, "handleEvent", event.newInstance( + ForgeRegistries.Keys.INGREDIENT_SERIALIZERS, ForgeRegistries.INGREDIENT_SERIALIZERS.get(), null)); + } + + private static void createForgeRegistry(ResourceKey key) throws ReflectiveOperationException { + var registries = ForgeRegistries.class.getDeclaredField("registries"); + registries.setAccessible(true); + DeferredRegister register = ((List) registries.get(null)).stream() + .map(DeferredRegister.class::cast).filter(value -> key.equals(value.getRegistryKey())) + .findFirst().orElseThrow(); + var event = new NewRegistryEvent(); + invokeRegistryPhase(register, "createRegistry", event); + var fill = NewRegistryEvent.class.getDeclaredMethod("fill"); + fill.setAccessible(true); + fill.invoke(event); + } + + private static void invokeRegistryPhase(DeferredRegister register, String phase, Object event) throws ReflectiveOperationException { // The launch-only EventBus transform is not present in plain JUnit. Class dispatcher = Class.forName(DeferredRegister.class.getName() + "$EventDispatcher"); @@ -67,6 +83,16 @@ private static void invokeRegistryPhase(DeferredRegister register, String pha method.invoke(constructor.newInstance(register), event); } + public static void bindItemTags(java.util.Map, + List>> tags) { + var registry = (net.minecraft.core.MappedRegistry)BuiltInRegistries.ITEM; + registry.unfreeze(); + registry.bindAllTagsToEmpty(); + BuiltInRegistries.ITEM.freeze(); + BuiltInRegistries.ITEM.prepareTagReload(new net.minecraft.tags.TagLoader.LoadResult<>( + net.minecraft.core.registries.Registries.ITEM, tags)).apply(); + } + private static final class NativeRegistries { static { SharedConstants.tryDetectVersion(); diff --git a/tools/generate-recipe-unlocks.js b/tools/generate-recipe-unlocks.js index 6053fc6..110df6a 100644 --- a/tools/generate-recipe-unlocks.js +++ b/tools/generate-recipe-unlocks.js @@ -14,8 +14,8 @@ const ignored = new Set(['item:minecraft:stick', 'item:minecraft:string', function choices(value) { if (Array.isArray(value)) return value.flatMap(choices); - if (value && value.tag) return [{tag: value.tag}]; - if (value && value.item) return [{item: value.item}]; + if (typeof value === 'string' && value.startsWith('#')) return [{tag: value.slice(1)}]; + if (typeof value === 'string') return [{item: value}]; throw new Error('Unsupported recipe ingredient: ' + JSON.stringify(value)); } diff --git a/tools/run-packaged-server-smoke.ps1 b/tools/run-packaged-server-smoke.ps1 index 67b4e57..83dc34f 100644 --- a/tools/run-packaged-server-smoke.ps1 +++ b/tools/run-packaged-server-smoke.ps1 @@ -13,9 +13,9 @@ $ErrorActionPreference = 'Stop' $project = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..')).Path $forge = (Resolve-Path -LiteralPath $ForgeInstall).Path $pins = Get-Content -Raw -LiteralPath (Join-Path $project 'gradle.properties') | ConvertFrom-StringData -if ($pins.minecraft_version -ne '1.21.1' -or $pins.forge_version -ne '52.1.0' -or +if ($pins.minecraft_version -ne '1.21.11' -or $pins.forge_version -ne '61.1.0' -or $pins.poweradvantage_source_ref -cnotmatch '^[0-9a-f]{40}$') { - throw 'An exact Forge 52.1.0 candidate and committed Power source pin are required.' + throw 'An exact Forge 61.1.0 candidate and committed Power source pin are required.' } $java = (Resolve-Path -LiteralPath $JavaExe).Path $javaVersion = (& $java -version 2>&1 | Out-String).Trim() @@ -34,10 +34,10 @@ if (!$PowerJar) { $PowerJar = Join-Path $project "../../PowerAdvantage/build/libs/basePower-$($pins.poweradvantage_version).jar" } if (!$BaseMetalsJar) { - $BaseMetalsJar = Join-Path $project "../../../../BaseMetals/.verification-basemetals1211/Advantage shared hook/build/libs/BaseMetals-$($pins.basemetals_version).jar" + $BaseMetalsJar = Join-Path $project "../../../../BaseMetals/.verification-basemetals12111/Advantage shared hook/build/libs/BaseMetals-$($pins.basemetals_version).jar" } if (!$OreSpawnJar) { - $OreSpawnJar = Join-Path $project "../../../../MinecraftMineralogy/OS1211/OreSpawn/build/libs/OreSpawn-$($pins.orespawn_version).jar" + $OreSpawnJar = Join-Path $project "../../../../MinecraftMineralogy/OS12111/OreSpawn/build/libs/OreSpawn-$($pins.orespawn_version).jar" } $mods = [ordered]@{ $steamName = @{ Path = (Join-Path $project "build/libs/$steamName"); Sha256 = $steamSha } @@ -57,7 +57,7 @@ $forgeJars = [ordered]@{ } foreach ($file in $forgeJars.Keys) { if ($forgeJars[$file] -cnotmatch '^[A-F0-9]{64}$') { - throw "Supply the coordinated Forge 52.1.0 runtime hash in gradle.properties: $file" + throw "Supply the coordinated Forge 61.1.0 runtime hash in gradle.properties: $file" } if ((Get-FileHash -LiteralPath (Join-Path $forge $file)).Hash -cne $forgeJars[$file]) { throw "Installed Forge runtime hash mismatch: $file" @@ -110,7 +110,7 @@ try { $start.RedirectStandardInput = $true $start.RedirectStandardOutput = $true $start.RedirectStandardError = $true - foreach ($argument in @('-Xms512m', '-Xmx3g', '-Djava.net.preferIPv6Addresses=system', '-jar', $shim, '--nogui')) { + foreach ($argument in @('-Xms512m', '-Xmx3g', '-XX:ActiveProcessorCount=4', '-Djava.net.preferIPv6Addresses=system', '-jar', $shim, '--nogui')) { $start.ArgumentList.Add($argument) } $process = [System.Diagnostics.Process]::Start($start) diff --git a/tools/verify-eclipse-ecj.ps1 b/tools/verify-eclipse-ecj.ps1 new file mode 100644 index 0000000..9744ef1 --- /dev/null +++ b/tools/verify-eclipse-ecj.ps1 @@ -0,0 +1,106 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory)][ValidatePattern('^[a-zA-Z0-9_-]+$')][string]$RunId, + [string]$JavaExe = "$env:USERPROFILE/.jdks/temurin-21.0.7+6/bin/java.exe", + [string]$CompilerJar = "$env:USERPROFILE/.p2/pool/plugins/org.eclipse.jdt.core.compiler.batch_3.46.0.v20260528-0407.jar" +) + +$ErrorActionPreference = 'Stop' +$project = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..')).Path +$java = (Resolve-Path -LiteralPath $JavaExe).Path +$compiler = (Resolve-Path -LiteralPath $CompilerJar).Path +$compilerHash = (Get-FileHash -LiteralPath $compiler -Algorithm SHA256).Hash +if ($compilerHash -cne 'D0D43F8E2D7003E5EFED612E2CBB5F01870043397D8F1BBE536FD9128F4FCBF7') { + throw 'The installed Eclipse ECJ 3.46.0 compiler does not match the qualified tool.' +} +$version = (& $java -version 2>&1 | Out-String).Trim() +if ($LASTEXITCODE -ne 0 -or $version -notmatch 'Temurin-21\.0\.7\+6') { throw "Unexpected JVM: $version" } +$pins = Get-Content -Raw -LiteralPath (Join-Path $project 'gradle.properties') | ConvertFrom-StringData +[xml]$classpath = Get-Content -Raw -LiteralPath (Join-Path $project '.classpath') +$entries = @($classpath.classpath.classpathentry) +$run = Join-Path $project "build/ecj-$RunId" +if (Test-Path -LiteralPath $run) { throw "Evidence already exists: $run" } +New-Item -ItemType Directory -Path $run | Out-Null +$receipt = [ordered]@{ + status = 'FAILED' + commit = (& git -C $project rev-parse HEAD) + java = $version + compiler = @{ path = $compiler; sha256 = $compilerHash } + classpathSha256 = (Get-FileHash -LiteralPath (Join-Path $project '.classpath') -Algorithm SHA256).Hash + powerSource = $pins.poweradvantage_source_ref + scopes = [ordered]@{} + finishedUtc = $null +} +function Attribute($entry, [string]$name) { + @($entry.attributes.attribute | Where-Object { $_.name -eq $name } | ForEach-Object value) -join ',' +} +function ProjectPath([string]$path) { + (Resolve-Path -LiteralPath $(if ([IO.Path]::IsPathRooted($path)) { $path } else { Join-Path $project $path })).Path +} +Push-Location -LiteralPath $project +try { + foreach ($scope in @('main', 'test', 'gameTestValidation')) { + $roots = @($entries | Where-Object { $_.kind -eq 'src' -and (Attribute $_ 'gradle_scope') -eq $scope }) + $sources = @($roots | ForEach-Object { + if ($_.GetAttribute('including') -or $_.GetAttribute('excluding')) { + throw "Unsupported Eclipse source filter on $($_.path)" + } + Get-ChildItem -LiteralPath (ProjectPath $_.path) -Filter *.java -Recurse | ForEach-Object FullName + } | Sort-Object -Unique) + if (!$sources.Count) { throw "Empty Eclipse $scope source scope" } + $libraries = @($entries | Where-Object { $_.kind -eq 'lib' -and + ((Attribute $_ 'gradle_used_by_scope').Split(',') -contains $scope) } | ForEach-Object { ProjectPath $_.path }) + foreach ($dependency in @( + @{ pattern = 'basePower-*.jar'; hash = $pins.poweradvantage_deobf_sha256 }, + @{ pattern = 'BaseMetals-*.jar'; hash = $pins.basemetals_deobf_sha256 }, + @{ pattern = 'OreSpawn-*.jar'; hash = $pins.orespawn_deobf_sha256 })) { + $matches = @($libraries | Where-Object { [IO.Path]::GetFileName($_) -like $dependency.pattern }) + if ($matches.Count -ne 1 -or (Get-FileHash -LiteralPath $matches[0] -Algorithm SHA256).Hash -cne $dependency.hash) { + throw "Eclipse $scope is missing the exact dependency $($dependency.pattern)" + } + } + if ($scope -ne 'test' -and @($libraries | Where-Object { [IO.Path]::GetFileName($_) -like 'junit-*' }).Count) { + throw "JUnit leaked into the Eclipse $scope compiler scope" + } + $output = Join-Path $run $scope + New-Item -ItemType Directory -Path $output | Out-Null + if ($scope -ne 'main') { $libraries = @((Join-Path $run 'main')) + $libraries } + $arguments = @('-21', '-proc:none', '-encoding', 'UTF-8', '-classpath', ('"' + ($libraries -join ';') + '"'), + '-d', ('"' + $output + '"')) + @($sources | ForEach-Object { '"' + $_ + '"' }) + $argumentFile = Join-Path $run "$scope.args" + $arguments | Set-Content -LiteralPath $argumentFile -Encoding utf8 + $receipt.scopes[$scope] = [ordered]@{ + sourceCount = $sources.Count + sources = @($sources | ForEach-Object { @{ path = $_; sha256 = (Get-FileHash -LiteralPath $_ -Algorithm SHA256).Hash } }) + libraries = $libraries + exitCode = $null + } + & $java -XX:ActiveProcessorCount=4 -Xmx1G -jar $compiler "@$argumentFile" *> (Join-Path $run "$scope.log") + $receipt.scopes[$scope].exitCode = $LASTEXITCODE + if ($LASTEXITCODE -ne 0) { throw "ECJ $scope failed; see $run/$scope.log" } + } + $bootstrap = ProjectPath '../../PowerAdvantage/build/unit-bootstrap/forge-native-unit-bootstrap.jar' + if ((Get-FileHash -LiteralPath $bootstrap -Algorithm SHA256).Hash -cne $pins.poweradvantage_test_bootstrap_sha256) { + throw 'Pinned native unit bootstrap changed' + } + $runtime = @((Join-Path $run 'test'), (Join-Path $run 'main'), (Join-Path $project 'build/resources/main')) + $runtime += @($receipt.scopes.test.libraries | Where-Object { $_ -ne (Join-Path $run 'main') }) + $tests = @($receipt.scopes.test.sources | Where-Object { $_.path -like '*Test.java' } | ForEach-Object { + $source = Get-Content -Raw -LiteralPath $_.path + if ($source -notmatch '(?m)^\s*package\s+([\w.]+)\s*;') { throw "Missing package in $($_.path)" } + $Matches[1] + '.' + [IO.Path]::GetFileNameWithoutExtension($_.path) + }) + $unitReceipt = Join-Path $run 'junit.json' + & $java -XX:ActiveProcessorCount=4 -Xmx1536m "-javaagent:$bootstrap" --add-opens java.base/java.lang.invoke=ALL-UNNAMED ` + -cp ($runtime -join ';') zone.moddev.mc.basesteam.testing.EclipseUnitLauncher $unitReceipt @tests ` + *> (Join-Path $run 'junit.log') + $receipt.unitExitCode = $LASTEXITCODE + if ($LASTEXITCODE -ne 0) { throw "ECJ-compiled JUnit failed; see $run/junit.log" } + $receipt.unit = Get-Content -Raw -LiteralPath $unitReceipt | ConvertFrom-Json + $receipt.status = 'ECJ_ALL_SCOPES_AND_UNIT_PASS' + Write-Output "$($receipt.status): $run" +} finally { + $receipt.finishedUtc = [DateTime]::UtcNow.ToString('o') + $receipt | ConvertTo-Json -Depth 9 | Set-Content -LiteralPath (Join-Path $run 'receipt.json') -Encoding utf8 + Pop-Location +} diff --git a/tools/verify-native-port.js b/tools/verify-native-port.js index ce7637a..d5f1c30 100644 --- a/tools/verify-native-port.js +++ b/tools/verify-native-port.js @@ -25,9 +25,10 @@ for (const name of files(resources).filter(name => /\.(json|mcmeta)$/.test(name) json.set(name, JSON.parse(text)); } assert.deepEqual(json.get(`${resources}/pack.mcmeta`).pack, { - pack_format: 34, supported_formats: {min_inclusive: 34, max_inclusive: 48}, description: 'baseSteam' + min_format: [94, 1], max_format: [94, 1], description: 'baseSteam' }); const metadata = read(`${resources}/META-INF/mods.toml`); +assert.match(metadata, /^showAsResourcePack=false$/m); assert.match(metadata, /modId="basesteam"/); assert.match(metadata, /modId="basepower"\r?\nmandatory=true\r?\nversionRange="\[\$\{poweradvantage_version\},3\.0\.0\)"/); assert.match(metadata, /modId="basemetals"\r?\nmandatory=false/); @@ -105,6 +106,13 @@ for (const id of pickaxe.values.map(value => value.split(':')[1]).filter(id => ! } const tests = files('src/test/java').filter(name => name.endsWith('.java')); const testCount = tests.reduce((sum, name) => sum + (read(name).match(/@Test\b/g) || []).length, 0); -console.log(`BASESTEAM_NATIVE1211_SOURCE_PASS json=${json.size} java=${source.length} factories=${ids.length} harvest=${pickaxe.values.length} testMethods=${testCount}`); +for (const id of [...pickaxe.values.map(value => value.split(':')[1]), + 'steam_governor', 'steam_drill_bit', 'blackpowder_cartridge', 'musket']) { + assert(json.has(`${assets}/items/${id}.json`), `Missing native item definition: ${id}`); +} +for (const name of source) { + assert(!/ResourceLocation|net\.minecraftforge\.eventbus\.api\.(?:IEventBus|SubscribeEvent)/.test(read(name)), name); +} +console.log(`BASESTEAM_NATIVE12111_SOURCE_PASS json=${json.size} java=${source.length} factories=${ids.length} harvest=${pickaxe.values.length} testMethods=${testCount}`); console.log('Source checks only. Compilation, executed tests, packaged runtime and manual acceptance are separate.'); require('./verify-enchantment-data'); From 63eef004da9554f3607810b612491084a546d4d2 Mon Sep 17 00:00:00 2001 From: JohnBraham Date: Wed, 11 Nov 2026 17:35:28 +0000 Subject: [PATCH 2/8] Pin repaired native integration and verify packaged Steam reopen --- docs/PORT12111-STATUS.md | 48 ++++++++++--- gradle.properties | 12 ++-- tools/run-packaged-server-smoke.ps1 | 108 ++++++++++++++++++---------- 3 files changed, 117 insertions(+), 51 deletions(-) diff --git a/docs/PORT12111-STATUS.md b/docs/PORT12111-STATUS.md index 885a66a..a4e37ad 100644 --- a/docs/PORT12111-STATUS.md +++ b/docs/PORT12111-STATUS.md @@ -1,5 +1,13 @@ # Steam on Minecraft 1.21.11 +The coordinated local Power/Base Metals callback fixes are pinned below. +The forced 43-task source/release suite, actual Eclipse ECJ scopes, all four +GameTests and both packaged save/reopen profiles pass against these pins. +The ordinary Steam player jar remains byte-identical to the frozen candidate: +5B4D7743B5507060D4D11E77944814E66257AAC9089C47E423E3E3A165912648. +Exact dependency hashes and source commits are build-audit pins, not embedded +manifest fields; the runtime metadata retains the same dependency versions. + Local source port 2.2.16.121111 targets Forge 61.1.0, official mappings, MCP 20251223.124241, Java 21, ForgeGradle 7.0.34 and Gradle 9.6.1. The starting source is manually accepted 1.21.1 commit @@ -7,8 +15,8 @@ The starting source is manually accepted 1.21.1 commit GameTests are a baseline, not qualification of this port. The approved local basePower 2.4.15.121111 integration candidate is pinned to -398439074a60bf9b8f8872f92485cffc4aa96be9 and native jar SHA-256 -0240E032075AF6B158887FBF98CE038504942329527036B82FD7CF9EEE602503 +1f5085f1cc7adbcb1bd8cd1062601d490e5749f3 and native jar SHA-256 +D8BA2501148845FFE1659569D1CB00C242C462123CB5A6B89D68FC346CABA7E9 with exact native unit-bootstrap and registry-support source hashes. The default local dependency paths pass without path overrides. Public release file IDs for local-only candidates remain empty and hosted publication gates @@ -18,9 +26,16 @@ independent pivot and sweep geometry checks. The deprecated global overload retains its signature and delegates through the current native machine screen only during its actual decoration callback. No shared Power API or runtime jar change was needed. Submission and failure paths restore the caller matrix. -The local Base Metals 3.1.2.121111 shared-hook candidate is pinned to source -e6b869ffe7d6fd6a011a99f1844d2a7995394daf and jar SHA-256 +The local Base Metals 3.1.2.121111 shared-hook callback fix is pinned to source +616d527e50e4bf0137eeb9c44fa586e34a0452a0 and jar SHA-256 +598D3FB0FF956EBCC67FE477A01CE393C75D0013AF0CE00A79E2072B63E9F2BA. +Earlier receipts used Power source 398439074a60bf9b8f8872f92485cffc4aa96be9 +and jar 0240E032075AF6B158887FBF98CE038504942329527036B82FD7CF9EEE602503, +with Base Metals source e6b869ffe7d6fd6a011a99f1844d2a7995394daf and jar 6D24D760E8E025960095DE04C51E6B79CFF93071618FC0B6BF64E05C346CC097. +The parent's real-handler regression found a cancellable RETURN ordering +defect in those shared inputs; they and their receipts remain preserved under +build/native-pinned-28, not relabelled as fixed or used for current proof. OreSpawn 4.1.0.121111 is pinned to the published native jar. Forge installer, server shim, server and universal hashes were checked against the authentic local runtime seed and the proven native Base Metals reference. @@ -54,8 +69,18 @@ All four native GameTests pass, selected by basesteam:* with exact named XML results and no failures/skips. Forge 61's test-function registry and native test_instance resources are confined to the isolated GameTest source scope. The first launch's built-in-only one-test result correctly failed the gate. -Packaged runtime, combined migration/endurance, -cold/path-with-spaces reproducibility and manual acceptance remain open. +Packaged dedicated startup/save/reopen passes in two separate JVMs for both +the with-Base-Metals and without-Base-Metals profiles. Every process verifies +the exact copied candidate hashes and checks latest/stdout/stderr without +filtering loader/data errors. These profiles explicitly select native NIO +transport on Windows. The earlier default native-availability selection failed +the strict stderr gate on the platform's Log4j/Netty unsupported kqueue/epoll +probe; its failed receipt is retained, not waived as a clean default run. +The parent's pure-platform receipt independently shows the same diagnostic. +Cold/path-with-spaces reproducibility is recorded separately under +build/repro path with spaces 34, using the repaired inputs and no dependency +path overrides. Combined migration/endurance and manual acceptance remain +separate parent/user gates; these isolated checks do not establish them. Nothing has been pushed, tagged or published. Merged 1.21.1 source b145f8d1a7c2a148f8f259faec7b203cf146b6b5 changes only @@ -72,7 +97,7 @@ Independent checks pass for the source/resource contracts, 24 recipe unlocks, 33 normalized recipe payloads and 113 unchanged protected resource files. The two companion native jar audits pass. Eclipse metadata generation passes. The earlier 27-test receipt with two pack-codec failures is retained. Current -native resource-reader tests verify 160 client and 91 server files byte for +native resource-reader tests verify 161 client and 91 server files byte for byte from a resource-only fixture jar, with no WARN/ERROR diagnostics. The refreshed independent batch compiles with javac and actual Eclipse ECJ and passes 32 tests. Native Ingredient.CODEC controls cover strings, tags and @@ -81,8 +106,13 @@ malformed selectors. ItemPredicate.CODEC controls prove the native discovery predicates remain restrictive and expose the ignored old item field. The generator's 24 checked-in unlocks remain unchanged and --check passes. Earlier evidence is under ignored build/native-independent-26 and -build/steam-native-compile-22. Current full-source/archive evidence is under -build/native-pinned-28 and build/ecj-native61-first. Failed attempts remain +build/steam-native-compile-22. Current repaired-pin evidence is under +build/native-repaired-33, build/ecj-native61-repaired-33, +build/steam-gametest-native61-repaired-33 and +evidence/packaged-server-native61-repaired-33-{with,without}-metals-nio. +All three rebuilt Steam archives match the frozen artifacts byte for byte. +Earlier source/archive/ECJ and old-pin cold receipts remain under +build/native-pinned-28 and build/repro path with spaces 32. Failed attempts remain alongside later runs, including the old ValueOutput namespace false positive and initial atlas-fixture setup failures. No gameplay or world-upgrade qualification is implied by source/asset checks. diff --git a/gradle.properties b/gradle.properties index 805f09e..422096b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -43,20 +43,20 @@ cf_versions=1.21.11 poweradvantage_version=2.4.15.121111 poweradvantage_source_repository=MinecraftModDevelopmentMods/basePower # Exact approved LOCAL integration candidate; no public 1.21.11 release is implied. -poweradvantage_source_ref=398439074a60bf9b8f8872f92485cffc4aa96be9 +poweradvantage_source_ref=1f5085f1cc7adbcb1bd8cd1062601d490e5749f3 poweradvantage_public_file_id= -poweradvantage_deobf_sha256=0240E032075AF6B158887FBF98CE038504942329527036B82FD7CF9EEE602503 +poweradvantage_deobf_sha256=D8BA2501148845FFE1659569D1CB00C242C462123CB5A6B89D68FC346CABA7E9 # Forge 61 uses the same native jar for development and packaged runtime. -poweradvantage_player_sha256=0240E032075AF6B158887FBF98CE038504942329527036B82FD7CF9EEE602503 +poweradvantage_player_sha256=D8BA2501148845FFE1659569D1CB00C242C462123CB5A6B89D68FC346CABA7E9 poweradvantage_test_bootstrap_sha256=FDE28801E41787E05212F3DEF7FAF34C6859402A22B71866D15B468C300DA883 poweradvantage_test_bootstrap_source_sha256=CDE40D09A40DB95F293BC56723E70B321C205850275696BEBFC2A575A8B7FE48 poweradvantage_test_registry_source_sha256=01DDE50C9874C2DC501BE2F699CD8D16D6974FACFAAC1B803434CA8C64227ABF basemetals_version=3.1.2.121111 -basemetals_source_commit=e6b869ffe7d6fd6a011a99f1844d2a7995394daf +basemetals_source_commit=616d527e50e4bf0137eeb9c44fa586e34a0452a0 basemetals_public_file_id= -basemetals_deobf_sha256=6D24D760E8E025960095DE04C51E6B79CFF93071618FC0B6BF64E05C346CC097 -basemetals_player_sha256=6D24D760E8E025960095DE04C51E6B79CFF93071618FC0B6BF64E05C346CC097 +basemetals_deobf_sha256=598D3FB0FF956EBCC67FE477A01CE393C75D0013AF0CE00A79E2072B63E9F2BA +basemetals_player_sha256=598D3FB0FF956EBCC67FE477A01CE393C75D0013AF0CE00A79E2072B63E9F2BA orespawn_version=4.1.0.121111 orespawn_source_commit=ecad75b24309a344f712d898d717c6a8823fc9f2 diff --git a/tools/run-packaged-server-smoke.ps1 b/tools/run-packaged-server-smoke.ps1 index 83dc34f..3015f16 100644 --- a/tools/run-packaged-server-smoke.ps1 +++ b/tools/run-packaged-server-smoke.ps1 @@ -6,7 +6,9 @@ param( [string]$PowerJar, [string]$BaseMetalsJar, [string]$OreSpawnJar, - [switch]$WithoutBaseMetals + [switch]$WithoutBaseMetals, + [switch]$UseNioTransport, + [switch]$Reopen ) $ErrorActionPreference = 'Stop' @@ -84,6 +86,8 @@ $receipt = [ordered]@{ java = $javaVersion mods = $mods world = (Join-Path $run 'steam-smoke') + transport = $(if ($UseNioTransport) { 'NIO' } else { 'native-availability-selection' }) + processes = @() finishedUtc = $null } $process = $null @@ -100,47 +104,79 @@ try { Set-Content -LiteralPath (Join-Path $run 'eula.txt') -Value 'eula=true' -Encoding ascii Set-Content -LiteralPath (Join-Path $run 'server.properties') -Encoding ascii -Value @( 'server-ip=127.0.0.1', 'server-port=0', 'level-name=steam-smoke', + "use-native-transport=$((!$UseNioTransport).ToString().ToLowerInvariant())", 'level-type=minecraft:flat', 'generate-structures=false', 'view-distance=2', 'simulation-distance=2', 'generator-settings={"layers":[{"height":1,"block":"minecraft:bedrock"},{"height":2,"block":"minecraft:dirt"},{"height":1,"block":"minecraft:grass_block"}],"biome":"minecraft:plains"}' ) - $start = [System.Diagnostics.ProcessStartInfo]::new($java) - $start.WorkingDirectory = $run - $start.UseShellExecute = $false - $start.CreateNoWindow = $true - $start.RedirectStandardInput = $true - $start.RedirectStandardOutput = $true - $start.RedirectStandardError = $true - foreach ($argument in @('-Xms512m', '-Xmx3g', '-XX:ActiveProcessorCount=4', '-Djava.net.preferIPv6Addresses=system', '-jar', $shim, '--nogui')) { - $start.ArgumentList.Add($argument) - } - $process = [System.Diagnostics.Process]::Start($start) - $stdout = $process.StandardOutput.ReadToEndAsync() - $stderr = $process.StandardError.ReadToEndAsync() - $latest = Join-Path $run 'logs/latest.log' - $deadline = [DateTime]::UtcNow.AddMinutes(3) - do { - if ($process.HasExited) { throw "Forge exited before ready: $($process.ExitCode)" } - $log = if (Test-Path -LiteralPath $latest) { Get-Content -Raw -LiteralPath $latest } - if ($log -match 'Done \([^)]+\)!') { break } - Start-Sleep -Seconds 1 - } while ([DateTime]::UtcNow -lt $deadline) - if ($log -notmatch 'Done \([^)]+\)!') { throw 'Timed out waiting for a fresh Forge world.' } - $process.StandardInput.WriteLine('stop') - if (!$process.WaitForExit(60000) -or $process.ExitCode -ne 0) { throw 'Forge did not stop cleanly.' } - $log = Get-Content -Raw -LiteralPath $latest - if ($log -notmatch "baseSteam $([regex]::Escape($pins.mod_version)) common setup complete" -or - $log -match '\[[^\]\r\n]*/(?:ERROR|FATAL)\]|NoClassDefFoundError') { - throw 'Missing Steam setup or a relevant loader/data error appeared in the server log.' + foreach ($phase in $(if ($Reopen) { @('startup', 'reopen') } else { @('startup') })) { + foreach ($name in $mods.Keys) { + if ((Get-FileHash -LiteralPath (Join-Path $run "mods/$name")).Hash -cne $mods[$name].Sha256) { + throw "Runtime jar changed before $phase : $name" + } + } + $phaseReceipt = [ordered]@{ phase = $phase; status = 'FAILED'; pid = $null; exitCode = $null; levelDatSha256 = $null } + $receipt.processes += $phaseReceipt + $process = $null + try { + $latest = Join-Path $run 'logs/latest.log' + if ($phase -eq 'reopen' -and (Test-Path -LiteralPath $latest)) { + Move-Item -LiteralPath $latest -Destination (Join-Path $run 'startup-original-latest.log') + } + $start = [System.Diagnostics.ProcessStartInfo]::new($java) + $start.WorkingDirectory = $run + $start.UseShellExecute = $false + $start.CreateNoWindow = $true + $start.RedirectStandardInput = $true + $start.RedirectStandardOutput = $true + $start.RedirectStandardError = $true + foreach ($argument in @('-Xms512m', '-Xmx3g', '-XX:ActiveProcessorCount=4', '-Djava.net.preferIPv6Addresses=system', '-jar', $shim, '--nogui')) { + $start.ArgumentList.Add($argument) + } + $process = [System.Diagnostics.Process]::Start($start) + $phaseReceipt.pid = $process.Id + $stdout = $process.StandardOutput.ReadToEndAsync() + $stderr = $process.StandardError.ReadToEndAsync() + $latest = Join-Path $run 'logs/latest.log' + $deadline = [DateTime]::UtcNow.AddMinutes(3) + $log = '' + do { + if ($process.HasExited) { throw "Forge exited before $phase ready: $($process.ExitCode)" } + if (Test-Path -LiteralPath $latest) { $log = Get-Content -Raw -LiteralPath $latest } + if ($log -match 'Done \([^)]+\)!') { break } + Start-Sleep -Seconds 1 + } while ([DateTime]::UtcNow -lt $deadline) + if ($log -notmatch 'Done \([^)]+\)!') { throw "Timed out waiting for Forge $phase." } + $process.StandardInput.WriteLine('stop') + if (!$process.WaitForExit(60000) -or $process.ExitCode -ne 0) { throw "Forge $phase did not stop cleanly." } + $phaseReceipt.exitCode = $process.ExitCode + $log = Get-Content -Raw -LiteralPath $latest + Copy-Item -LiteralPath $latest -Destination (Join-Path $run "$phase-latest.log") + $allLogs = $log + $stdout.GetAwaiter().GetResult() + $stderr.GetAwaiter().GetResult() + if ($log -notmatch "baseSteam $([regex]::Escape($pins.mod_version)) common setup complete" -or + $allLogs -match '\[[^\]\r\n]*/(?:ERROR|FATAL)\]|NoClassDefFoundError|Exception in thread') { + throw "Missing Steam setup or a loader/data error appeared during $phase." + } + $levelDat = Join-Path $run 'steam-smoke/level.dat' + if (!(Test-Path -LiteralPath $levelDat) -or (Get-Item -LiteralPath $levelDat).Length -eq 0) { + throw "Forge $phase did not save its world." + } + $phaseReceipt.levelDatSha256 = (Get-FileHash -LiteralPath $levelDat -Algorithm SHA256).Hash + $phaseReceipt.status = 'START_SAVE_STOP_PASS' + } finally { + if ($process) { + if (!$process.HasExited) { $process.Kill($true); $process.WaitForExit() } + $suffix = if ($phase -eq 'startup') { '' } else { "-$phase" } + Set-Content -LiteralPath (Join-Path $run "stdout$suffix.log") -Value $stdout.GetAwaiter().GetResult() + Set-Content -LiteralPath (Join-Path $run "stderr$suffix.log") -Value $stderr.GetAwaiter().GetResult() + $phaseReceipt.exitCode = $process.ExitCode + $process.Dispose() + $process = $null + } + } } - $receipt.status = 'PACKAGED_DEDICATED_SERVER_PASS' + $receipt.status = if ($Reopen) { 'PACKAGED_DEDICATED_SERVER_REOPEN_PASS' } else { 'PACKAGED_DEDICATED_SERVER_PASS' } Write-Output "$($receipt.status): $run" } finally { - if ($process) { - if (!$process.HasExited) { $process.Kill($true); $process.WaitForExit() } - Set-Content -LiteralPath (Join-Path $run 'stdout.log') -Value $stdout.GetAwaiter().GetResult() - Set-Content -LiteralPath (Join-Path $run 'stderr.log') -Value $stderr.GetAwaiter().GetResult() - $process.Dispose() - } $receipt.finishedUtc = [DateTime]::UtcNow.ToString('o') $receipt | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $run 'receipt.json') } From 94ae4ab265a622ff3098e88049321144cc54eb87 Mon Sep 17 00:00:00 2001 From: JohnBraham Date: Wed, 11 Nov 2026 18:00:20 +0000 Subject: [PATCH 3/8] Pin qualified Power B vanilla registry integration --- docs/PORT12111-STATUS.md | 24 ++++++++++++++---------- gradle.properties | 6 +++--- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/docs/PORT12111-STATUS.md b/docs/PORT12111-STATUS.md index a4e37ad..9b94728 100644 --- a/docs/PORT12111-STATUS.md +++ b/docs/PORT12111-STATUS.md @@ -1,6 +1,8 @@ # Steam on Minecraft 1.21.11 -The coordinated local Power/Base Metals callback fixes are pinned below. +Candidate B pins the local Power vanilla-registry delta fix and the Base +Metals callback fix below. Candidate A and its evidence remain preserved +separately under build/qualified-A-63eef004; its results are not relabelled B. The forced 43-task source/release suite, actual Eclipse ECJ scopes, all four GameTests and both packaged save/reopen profiles pass against these pins. The ordinary Steam player jar remains byte-identical to the frozen candidate: @@ -15,8 +17,8 @@ The starting source is manually accepted 1.21.1 commit GameTests are a baseline, not qualification of this port. The approved local basePower 2.4.15.121111 integration candidate is pinned to -1f5085f1cc7adbcb1bd8cd1062601d490e5749f3 and native jar SHA-256 -D8BA2501148845FFE1659569D1CB00C242C462123CB5A6B89D68FC346CABA7E9 +3c94944d300462badfbd88cef9f5f1dd6a51c32a and native jar SHA-256 +71E0E84F217AA33F4F190D26A44D236461C95BE4D5A3832B5DED214B883A3AD6 with exact native unit-bootstrap and registry-support source hashes. The default local dependency paths pass without path overrides. Public release file IDs for local-only candidates remain empty and hosted publication gates @@ -77,9 +79,11 @@ transport on Windows. The earlier default native-availability selection failed the strict stderr gate on the platform's Log4j/Netty unsupported kqueue/epoll probe; its failed receipt is retained, not waived as a clean default run. The parent's pure-platform receipt independently shows the same diagnostic. -Cold/path-with-spaces reproducibility is recorded separately under -build/repro path with spaces 34, using the repaired inputs and no dependency -path overrides. Combined migration/endurance and manual acceptance remain +The empty-cache platform proof is preserved under build/repro path with +spaces 34. B's independent path-with-spaces reproduction is recorded under +build/repro path with spaces 36, with a fresh source stage and the same +native-engine cache, not another cold download. It uses B's exact pins and +no dependency path overrides. Combined migration/endurance and manual acceptance remain separate parent/user gates; these isolated checks do not establish them. Nothing has been pushed, tagged or published. @@ -106,10 +110,10 @@ malformed selectors. ItemPredicate.CODEC controls prove the native discovery predicates remain restrictive and expose the ignored old item field. The generator's 24 checked-in unlocks remain unchanged and --check passes. Earlier evidence is under ignored build/native-independent-26 and -build/steam-native-compile-22. Current repaired-pin evidence is under -build/native-repaired-33, build/ecj-native61-repaired-33, -build/steam-gametest-native61-repaired-33 and -evidence/packaged-server-native61-repaired-33-{with,without}-metals-nio. +build/steam-native-compile-22. Current B-pin evidence is under +build/native-B-35, build/ecj-native61-B-35, +build/steam-gametest-native61-B-35 and +evidence/packaged-server-native61-B-35-{with,without}-metals-nio. All three rebuilt Steam archives match the frozen artifacts byte for byte. Earlier source/archive/ECJ and old-pin cold receipts remain under build/native-pinned-28 and build/repro path with spaces 32. Failed attempts remain diff --git a/gradle.properties b/gradle.properties index 422096b..fe806df 100644 --- a/gradle.properties +++ b/gradle.properties @@ -43,11 +43,11 @@ cf_versions=1.21.11 poweradvantage_version=2.4.15.121111 poweradvantage_source_repository=MinecraftModDevelopmentMods/basePower # Exact approved LOCAL integration candidate; no public 1.21.11 release is implied. -poweradvantage_source_ref=1f5085f1cc7adbcb1bd8cd1062601d490e5749f3 +poweradvantage_source_ref=3c94944d300462badfbd88cef9f5f1dd6a51c32a poweradvantage_public_file_id= -poweradvantage_deobf_sha256=D8BA2501148845FFE1659569D1CB00C242C462123CB5A6B89D68FC346CABA7E9 +poweradvantage_deobf_sha256=71E0E84F217AA33F4F190D26A44D236461C95BE4D5A3832B5DED214B883A3AD6 # Forge 61 uses the same native jar for development and packaged runtime. -poweradvantage_player_sha256=D8BA2501148845FFE1659569D1CB00C242C462123CB5A6B89D68FC346CABA7E9 +poweradvantage_player_sha256=71E0E84F217AA33F4F190D26A44D236461C95BE4D5A3832B5DED214B883A3AD6 poweradvantage_test_bootstrap_sha256=FDE28801E41787E05212F3DEF7FAF34C6859402A22B71866D15B468C300DA883 poweradvantage_test_bootstrap_source_sha256=CDE40D09A40DB95F293BC56723E70B321C205850275696BEBFC2A575A8B7FE48 poweradvantage_test_registry_source_sha256=01DDE50C9874C2DC501BE2F699CD8D16D6974FACFAAC1B803434CA8C64227ABF From dba0d7cb0ecbda10949c8447962c9b7d7b655ab4 Mon Sep 17 00:00:00 2001 From: JohnBraham Date: Wed, 11 Nov 2026 19:09:01 +0000 Subject: [PATCH 4/8] Qualify historical C2 Steam atlas and native client contracts --- CHANGELOG.txt | 4 +- README.md | 6 +- build.gradle | 39 +++-- docs/PORT12111-STATUS.md | 75 ++++++--- gradle.properties | 6 +- .../assets/minecraft/atlases/items.json | 2 +- .../NativeBlockItemTranslationTest.java | 129 +++++++++++++++ .../NativeItemAtlasContractTest.java | 156 ++++++++++++------ 8 files changed, 330 insertions(+), 87 deletions(-) create mode 100644 src/test/java/zone/moddev/mc/basesteam/resources/NativeBlockItemTranslationTest.java diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 57dae58..5575b47 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -4,8 +4,8 @@ baseSteam 2.2.16.121111 (local Minecraft 1.21.11 source port) - Adapt native registration, persistence, recipes, item definitions and drill rendering. - Retain registry identities, legacy state keys, recipe payloads and configuration defaults. - Use native Forge hidden-client pack metadata with data-format 94.1 at the mod root. -- Stitch the drill-bit sprite into the native item atlas as well as the block atlas. -- Local source and archive gates pass; runtime and combined upgrade qualification remains open. +- Keep block/material sprites, including the drill bit, out of the native item atlas; retain legacy item-icon lookup compatibility. +- Local source, archive, compiler, GameTest and packaged reopen gates pass; combined world upgrades, endurance and manual acceptance are separate. baseSteam 2.2.15.121011 (Minecraft 1.21.1 / Forge) diff --git a/README.md b/README.md index 8b75717..85ae7d5 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ OreSpawn 4.1.0.121111 as separate native jars, not bundled copies. Exact local source, jar and test-bootstrap pins are in `gradle.properties`. They are checked before compilation and tests. Local approval does not establish public availability. The Base Metals shared hook and approved Power -integration commit 398439074a60bf9b8f8872f92485cffc4aa96be9 are pinned locally. +integration commit a1cd197b4b6c3fc9c5d56da254d0f0ae9929fe8c are pinned locally. The mod root declares server data format 94.1. Forge supplies format 75.x metadata for its hidden client resource aggregate. Native resource-reader tests cover both paths without codec warnings; see @@ -45,8 +45,8 @@ $toolchains = @("-Porg.gradle.java.installations.paths=$jdks", ./gradlew.bat @toolchains check build deobfJar verifyReleaseArtifacts writeReleaseChecksums ./gradlew.bat @toolchains verifyEclipseProductionClasspath ./gradlew.bat @toolchains -PsteamGameTestRunId=candidate verifyPlayerDependencies runGameTestServer -./tools/run-packaged-server-smoke.ps1 -ForgeInstall $env:FORGE_12111_SERVER_INSTALL -RunId candidate-with-metals -./tools/run-packaged-server-smoke.ps1 -ForgeInstall $env:FORGE_12111_SERVER_INSTALL -RunId candidate-without-metals -WithoutBaseMetals +./tools/run-packaged-server-smoke.ps1 -ForgeInstall $env:FORGE_12111_SERVER_INSTALL -RunId candidate-with-metals -UseNioTransport -Reopen +./tools/run-packaged-server-smoke.ps1 -ForgeInstall $env:FORGE_12111_SERVER_INSTALL -RunId candidate-without-metals -WithoutBaseMetals -UseNioTransport -Reopen ``` Independent source checks: `node tools/verify-native-port.js` and diff --git a/build.gradle b/build.gradle index 6b0f22e..16df66d 100644 --- a/build.gradle +++ b/build.gradle @@ -659,9 +659,11 @@ def blocksAtlasPath = 'assets/minecraft/atlases/blocks.json' def itemsAtlasPath = 'assets/minecraft/atlases/items.json' def steamAtlasPaths = [blocksAtlasPath, itemsAtlasPath] def steamDrillAtlas = [sources: [[type: 'single', resource: 'basesteam:entity/drill_bit']]] -def verifySteamDrillAtlas = { Object atlas -> - if (atlas != steamDrillAtlas) { - throw new GradleException('Steam block/item atlases must stitch only basesteam:entity/drill_bit as a single source') +def steamItemsAtlas = [sources: [[type: 'directory', source: 'items', prefix: 'items/']]] +def verifySteamAtlas = { String path, Object atlas -> + def expected = path == blocksAtlasPath ? steamDrillAtlas : steamItemsAtlas + if (!steamAtlasPaths.contains(path) || atlas != expected) { + throw new GradleException("Steam ${path} must contain only ${expected}; block and item sources must remain disjoint") } } def verifyPackagedAssetNamespaces = { ZipFile zip -> @@ -674,7 +676,7 @@ def verifyPackagedAssetNamespaces = { ZipFile zip -> } steamAtlasPaths.each { path -> zip.getInputStream(zip.getEntry(path)).withCloseable { input -> - verifySteamDrillAtlas(new JsonSlurper().parse(input, 'UTF-8')) + verifySteamAtlas(path, new JsonSlurper().parse(input, 'UTF-8')) } } } @@ -1356,27 +1358,40 @@ tasks.register('verifyModelAssets') { Set atlasPrefixes = ['block/', 'item/'] as Set Set singleSprites = [] as Set - steamAtlases.each { steamAtlas -> + steamAtlasPaths.each { String atlasPath -> + File steamAtlas = new File(resourceRoot, atlasPath) if (!steamAtlas.isFile()) throw new GradleException("Missing native Steam atlas ${steamAtlas}") def atlas = new JsonSlurper().parse(steamAtlas) - verifySteamDrillAtlas(atlas) - singleSprites.addAll(atlas.sources.collect { it.resource }) + verifySteamAtlas(atlasPath, atlas) + singleSprites.addAll(atlas.sources.findAll { it.type == 'single' }.collect { it.resource }) + atlasPrefixes.addAll(atlas.sources.findAll { it.type == 'directory' }.collect { it.prefix }) } + Set itemAtlasPrefixes = ['item/', 'items/'] as Set Set powerAdvantageAssets = new ZipFile(powerAdvantageDeobfJar).withCloseable { zip -> - Set sharedPrefixes = null + Map> sharedPrefixes = [:] steamAtlasPaths.each { atlasPath -> def atlasEntry = zip.getEntry(atlasPath) if (atlasEntry == null) throw new GradleException("basePower is missing native atlas ${atlasPath}") zip.getInputStream(atlasEntry).withCloseable { input -> def atlas = new JsonSlurper().parse(input, 'UTF-8') + if (atlasPath == itemsAtlasPath) verifySteamAtlas(atlasPath, atlas) // Power's directory sources preserve the existing texture IDs across namespaces. Set prefixes = atlas.sources.findAll { it.type == 'directory' && it.source instanceof String && it.prefix == it.source + '/' }.collect { it.prefix } as Set - sharedPrefixes = sharedPrefixes == null ? prefixes : sharedPrefixes.intersect(prefixes) + sharedPrefixes[atlasPath] = prefixes } } - atlasPrefixes.addAll(sharedPrefixes) + Set collisions = sharedPrefixes[blocksAtlasPath].intersect(sharedPrefixes[itemsAtlasPath]) + collisions.retainAll(['blocks/', 'materials/']) + if (!collisions.isEmpty()) { + throw new GradleException("basePower shadows block/material directories in the item atlas: ${collisions}") + } + if (!sharedPrefixes[blocksAtlasPath].containsAll(['blocks/', 'materials/', 'items/'])) { + throw new GradleException('basePower block atlas must retain legacy blocks/, materials/ and generated-item items/ sources') + } + sharedPrefixes.values().each { atlasPrefixes.addAll(it) } + itemAtlasPrefixes.addAll(sharedPrefixes[itemsAtlasPath]) zip.entries().collect { it.name } as Set } def verifyReference = { String reference, String assetType, String sourcePath -> @@ -1399,6 +1414,10 @@ tasks.register('verifyModelAssets') { && !atlasPrefixes.any { path.startsWith(it) }) { throw new GradleException("${sourcePath} references unstitched blocks atlas texture ${reference}") } + if (assetType == 'textures' && new File(sourcePath).toPath().startsWith(new File(modelRoot, 'block').toPath()) + && itemAtlasPrefixes.any { path.startsWith(it) }) { + throw new GradleException("${sourcePath} references an item-atlas texture from a block model: ${reference}") + } } fileTree(modelRoot) { include '**/*.json' }.files.sort().each { File modelFile -> diff --git a/docs/PORT12111-STATUS.md b/docs/PORT12111-STATUS.md index 9b94728..83294b5 100644 --- a/docs/PORT12111-STATUS.md +++ b/docs/PORT12111-STATUS.md @@ -1,12 +1,19 @@ # Steam on Minecraft 1.21.11 -Candidate B pins the local Power vanilla-registry delta fix and the Base -Metals callback fix below. Candidate A and its evidence remain preserved -separately under build/qualified-A-63eef004; its results are not relabelled B. +This is historical C2 candidate qualification against Power134F, not an +overall family release claim. The parent reports a later, broader chained +1.13 dimension route exposing a Power dynamic-biome-registry issue; its fix +and replacement pin are outside this preserved candidate's proof. + +Candidate C2 pins the local Power registry, BlockItem-name and atlas fixes +and the Base Metals callback fix below. A/B evidence remains preserved under +build/qualified-A-63eef004 and build/native-B-35, not relabelled C2. The forced 43-task source/release suite, actual Eclipse ECJ scopes, all four GameTests and both packaged save/reopen profiles pass against these pins. -The ordinary Steam player jar remains byte-identical to the frozen candidate: -5B4D7743B5507060D4D11E77944814E66257AAC9089C47E423E3E3A165912648. +The ordinary Steam player jar matches the frozen C2 client candidate: +25EE7784EAA180C77B7735B90928801F56A7342B5733B49222E2B0E72E5577F9. +Only assets/minecraft/atlases/items.json differs from the earlier 5B4D jar; +all production class payloads are unchanged. Exact dependency hashes and source commits are build-audit pins, not embedded manifest fields; the runtime metadata retains the same dependency versions. @@ -17,8 +24,8 @@ The starting source is manually accepted 1.21.1 commit GameTests are a baseline, not qualification of this port. The approved local basePower 2.4.15.121111 integration candidate is pinned to -3c94944d300462badfbd88cef9f5f1dd6a51c32a and native jar SHA-256 -71E0E84F217AA33F4F190D26A44D236461C95BE4D5A3832B5DED214B883A3AD6 +a1cd197b4b6c3fc9c5d56da254d0f0ae9929fe8c and native jar SHA-256 +134FF55595E86D450DBFB5531F389BF0BE81FE6347F1EB03765B571A1D488B59 with exact native unit-bootstrap and registry-support source hashes. The default local dependency paths pass without path overrides. Public release file IDs for local-only candidates remain empty and hosted publication gates @@ -63,7 +70,7 @@ this platform compatibility-label issue remains open, not silently waived. See FORGE61-RESOURCE-METADATA.md for parent integration guidance. The default local check/build/Javadoc/dependency/archive/checksum/Eclipse -metadata suite passes (43 tasks), with 242 unit tests and zero failures, +metadata suite passes (43 tasks), with 244 unit tests and zero failures, errors or skips. All three real Eclipse .classpath scopes compile using ECJ 3.46.0; those ECJ-compiled unit tests also pass with zero skips/assumptions. Both source compilers include production, unit and GameTest Java sources. @@ -80,9 +87,9 @@ the strict stderr gate on the platform's Log4j/Netty unsupported kqueue/epoll probe; its failed receipt is retained, not waived as a clean default run. The parent's pure-platform receipt independently shows the same diagnostic. The empty-cache platform proof is preserved under build/repro path with -spaces 34. B's independent path-with-spaces reproduction is recorded under -build/repro path with spaces 36, with a fresh source stage and the same -native-engine cache, not another cold download. It uses B's exact pins and +spaces 34. C2's independent path-with-spaces reproduction is recorded under +build/repro path with spaces 39, with a fresh source stage and the same +native-engine cache, not another cold download. It uses C2's exact pins and no dependency path overrides. Combined migration/endurance and manual acceptance remain separate parent/user gates; these isolated checks do not establish them. Nothing has been pushed, tagged or published. @@ -92,10 +99,28 @@ release pins and documentation from 9c5bf9b4610e06d3b5a6d5a0bde38d7516327d96. Historical release notes were carried forward without importing 1.21.1 pins or transferring its runtime acceptance to this port. -The native item atlas now explicitly includes basesteam:entity/drill_bit in -addition to the block atlas. A native SpriteSources/resource-reader test -checks every Steam model texture against the exact Power and Steam packs, -including the legacy material IDs, and an omitted-Power negative control. +The native item atlas contains only the items directory with prefix items/. +The drill single source remains exclusively in Steam's block atlas. Native +SpriteSources/resource-reader tests cover every model texture and reject +block/material sources in ITEMS and any item-atlas resolution of world-block +textures. Power's block atlas intentionally retains legacy items/ lookup; +true item-icon overlap is tested positively, not mistaken for the block/material +shadowing defect. The omitted-Power and malformed-source controls remain strict. +Both native BlockItem description and hover-name keys are checked through +the actual registration factory for all 17 blocks, with a missing-prefix +negative control. These proofs run in child JVMs so intrusive holders cannot +leak into later registry-freeze tests. The initial isolation failure is retained. + +The parent's actual frozen combined client run 20261111-182839-413cf9f7 +passes with this exact Steam jar, Power134F, BM598D and the other frozen +profile inputs: 94 items, 905 states, 39 GUIs, 351 caption checks, 355 rejected +negative controls, 26 fluid-gauge pixel cases, recipe-book grouping and zero +resource errors. The frozen profile hash is +9109C8904BE3D42185F5A636EC5F0EF6ED36D5DD5986F14E4B607EC03704D700. +Input copies/origins and the receipt hashes were checked independently; +build/native-C2-38/frozen-client-proof-receipt.json records the binding. +Worldless renderer geometry retains its reported manual gaps; automated +client resource proof is not manual gameplay or world-renderer acceptance. Independent checks pass for the source/resource contracts, 24 recipe unlocks, 33 normalized recipe payloads and 113 unchanged protected resource files. @@ -110,13 +135,21 @@ malformed selectors. ItemPredicate.CODEC controls prove the native discovery predicates remain restrictive and expose the ignored old item field. The generator's 24 checked-in unlocks remain unchanged and --check passes. Earlier evidence is under ignored build/native-independent-26 and -build/steam-native-compile-22. Current B-pin evidence is under -build/native-B-35, build/ecj-native61-B-35, -build/steam-gametest-native61-B-35 and -evidence/packaged-server-native61-B-35-{with,without}-metals-nio. -All three rebuilt Steam archives match the frozen artifacts byte for byte. +build/steam-native-compile-22. Current C2 evidence is under +build/native-C2-38, build/ecj-native61-C2-38-final, +build/steam-gametest-native61-C2-38-final and +evidence/packaged-server-native61-C2-38-{with,without}-metals-nio. +The C2 archives are compared against their exact graphics/client-bound bytes. Earlier source/archive/ECJ and old-pin cold receipts remain under build/native-pinned-28 and build/repro path with spaces 32. Failed attempts remain alongside later runs, including the old ValueOutput namespace false positive -and initial atlas-fixture setup failures. No gameplay or world-upgrade +and initial atlas-fixture setup failures. C2 also retains the initially +over-broad global atlas-disjointness failure; the approved native contract +forbids block/material shadowing while preserving legacy true-item lookup. +No gameplay or world-upgrade qualification is implied by source/asset checks. + +The parent's migration-probe module remains an unchanged 1.10/1.12 source +fixture writer and was not rebuilt by this child. Native C2 profiles instead +use the pinned native AdvantageWorksTestHarness; they do not load that legacy +probe. No native variant or unrun legacy-probe build is claimed as passing. diff --git a/gradle.properties b/gradle.properties index fe806df..debc58c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -43,11 +43,11 @@ cf_versions=1.21.11 poweradvantage_version=2.4.15.121111 poweradvantage_source_repository=MinecraftModDevelopmentMods/basePower # Exact approved LOCAL integration candidate; no public 1.21.11 release is implied. -poweradvantage_source_ref=3c94944d300462badfbd88cef9f5f1dd6a51c32a +poweradvantage_source_ref=a1cd197b4b6c3fc9c5d56da254d0f0ae9929fe8c poweradvantage_public_file_id= -poweradvantage_deobf_sha256=71E0E84F217AA33F4F190D26A44D236461C95BE4D5A3832B5DED214B883A3AD6 +poweradvantage_deobf_sha256=134FF55595E86D450DBFB5531F389BF0BE81FE6347F1EB03765B571A1D488B59 # Forge 61 uses the same native jar for development and packaged runtime. -poweradvantage_player_sha256=71E0E84F217AA33F4F190D26A44D236461C95BE4D5A3832B5DED214B883A3AD6 +poweradvantage_player_sha256=134FF55595E86D450DBFB5531F389BF0BE81FE6347F1EB03765B571A1D488B59 poweradvantage_test_bootstrap_sha256=FDE28801E41787E05212F3DEF7FAF34C6859402A22B71866D15B468C300DA883 poweradvantage_test_bootstrap_source_sha256=CDE40D09A40DB95F293BC56723E70B321C205850275696BEBFC2A575A8B7FE48 poweradvantage_test_registry_source_sha256=01DDE50C9874C2DC501BE2F699CD8D16D6974FACFAAC1B803434CA8C64227ABF diff --git a/src/main/resources/assets/minecraft/atlases/items.json b/src/main/resources/assets/minecraft/atlases/items.json index 79b6fa5..c2643ee 100644 --- a/src/main/resources/assets/minecraft/atlases/items.json +++ b/src/main/resources/assets/minecraft/atlases/items.json @@ -1,5 +1,5 @@ { "sources": [ - { "type": "single", "resource": "basesteam:entity/drill_bit" } + { "type": "directory", "source": "items", "prefix": "items/" } ] } diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/NativeBlockItemTranslationTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/NativeBlockItemTranslationTest.java new file mode 100644 index 0000000..8a45f4c --- /dev/null +++ b/src/test/java/zone/moddev/mc/basesteam/resources/NativeBlockItemTranslationTest.java @@ -0,0 +1,129 @@ +package zone.moddev.mc.basesteam.resources; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.mojang.serialization.Lifecycle; +import java.lang.management.ManagementFactory; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import net.minecraft.core.MappedRegistry; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; +import net.minecraft.network.chat.contents.TranslatableContents; +import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.Item; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.ForgeRegistry; +import net.minecraftforge.registries.RegisterEvent; +import org.junit.Test; +import zone.moddev.mc.basepower.init.ItemGroups; +import zone.moddev.mc.basesteam.init.Blocks; +import zone.moddev.mc.basesteam.testing.NativeTestBootstrap; +import static org.junit.Assert.*; + +public class NativeBlockItemTranslationTest { + @Test public void actualProductionBlockItemFactoryUsesTranslatedBlockKeys() throws Exception { + isolated("factory", "BLOCKITEM_FACTORY_17_PASS"); + } + + @Test public void omittedNativeBlockPrefixReproducesTheUntranslatedItemKey() throws Exception { + isolated("negative", "BLOCKITEM_MISSING_PREFIX_NEGATIVE_PASS"); + } + + private static void isolated(String mode, String marker) throws Exception { + Path root = Path.of("build/native-blockitem-translations").toAbsolutePath(); + Files.createDirectories(root); + Path evidence = Files.createTempDirectory(root, mode + "-"); + Path javaExecutable = Path.of(System.getProperty("java.home"), "bin", "java"); + if (!Files.isExecutable(javaExecutable)) javaExecutable = javaExecutable.resolveSibling("java.exe"); + String classpath = Arrays.stream(System.getProperty("basesteam.nativePackClasspath", + System.getProperty("java.class.path")).split(java.util.regex.Pattern.quote(java.io.File.pathSeparator))) + .map(entry -> new java.io.File(entry).getAbsolutePath()) + .collect(java.util.stream.Collectors.joining(java.io.File.pathSeparator)); + var command = new ArrayList<>(java.util.List.of(javaExecutable.toString(), "-XX:ActiveProcessorCount=4", "-Xmx512m", + "--add-opens", "java.base/java.lang.invoke=ALL-UNNAMED")); + command.addAll(ManagementFactory.getRuntimeMXBean().getInputArguments().stream() + .filter(argument -> argument.startsWith("-javaagent:")).toList()); + command.addAll(java.util.List.of("-cp", classpath, NativeBlockItemTranslationTest.class.getName(), mode)); + Path log = evidence.resolve("native-factory.log"); + Process process = new ProcessBuilder(command).redirectErrorStream(true).redirectOutput(log.toFile()).start(); + if (!process.waitFor(90, TimeUnit.SECONDS)) { + process.destroyForcibly().waitFor(); + throw new AssertionError("Native BlockItem proof timed out: " + log); + } + String output = Files.readString(log); + assertEquals(log + "\n" + output, 0, process.exitValue()); + assertTrue(log + "\n" + output, output.contains(marker)); + } + + public static void main(String[] args) throws Exception { + if (args.length != 1) throw new IllegalArgumentException("Expected factory or negative proof"); + NativeTestBootstrap.start(); + Blocks.init(); + switch (args[0]) { + case "factory" -> { + proveFactory(); + System.out.println("BLOCKITEM_FACTORY_17_PASS"); + } + case "negative" -> { + proveNegative(); + System.out.println("BLOCKITEM_MISSING_PREFIX_NEGATIVE_PASS"); + } + default -> throw new IllegalArgumentException("Unknown BlockItem proof: " + args[0]); + } + } + + @SuppressWarnings("unchecked") + private static void proveFactory() throws Exception { + MappedRegistry items = new MappedRegistry<>(Registries.ITEM, Lifecycle.stable()); + var constructor = RegisterEvent.class.getDeclaredConstructor(ResourceKey.class, ForgeRegistry.class, Registry.class); + constructor.setAccessible(true); + var visible = ItemGroups.class.getDeclaredField("VISIBLE_ITEMS"); + visible.setAccessible(true); + Set visibleItems = (Set)visible.get(null); + try { + Blocks.registerBlockItems(constructor.newInstance(ForgeRegistries.Keys.ITEMS, null, items)); + } finally { + for (Item item : items) visibleItems.remove(item); + } + assertEquals(Set.of("steam_pipe", "steam_track", "steam_boiler_coal", "steam_boiler_electric", + "steam_boiler_geothermal", "steam_tank", "steam_furnace", "steam_crusher", "steam_drill", + "drillbit", "platform", "steam_elevator", "steam_switch", "pump_pipe_steam", "steam_still", + "steam_pump", "steam_boiler_oil"), + items.keySet().stream().map(Identifier::getPath).collect(java.util.stream.Collectors.toSet())); + JsonObject english = english(); + for (Identifier id : items.keySet()) { + String key = "block.basesteam." + id.getPath(); + assertEquals("basesteam", id.getNamespace()); + assertTrue(items.getValue(id) instanceof BlockItem); + BlockItem item = (BlockItem)items.getValue(id); + assertEquals(key, item.getBlock().getDescriptionId()); + assertEquals(key, item.getDescriptionId()); + assertTrue(item.getDefaultInstance().getHoverName().getContents() instanceof TranslatableContents); + assertEquals(key, ((TranslatableContents)item.getDefaultInstance().getHoverName().getContents()).getKey()); + assertTrue("Untranslated native block item " + key, english.has(key)); + assertFalse(english.get(key).getAsString().isBlank()); + } + } + + private static void proveNegative() throws Exception { + BlockItem wrong = new BlockItem(Blocks.steam_pipe, new Item.Properties().setId(ResourceKey.create( + Registries.ITEM, Identifier.fromNamespaceAndPath("basesteam", "steam_pipe")))); + assertEquals("item.basesteam.steam_pipe", wrong.getDescriptionId()); + assertEquals("item.basesteam.steam_pipe", + ((TranslatableContents)wrong.getDefaultInstance().getHoverName().getContents()).getKey()); + assertFalse(english().has(wrong.getDescriptionId())); + assertTrue(english().has("block.basesteam.steam_pipe")); + } + + private static JsonObject english() throws Exception { + return JsonParser.parseString(Files.readString( + Path.of("src/main/resources/assets/basesteam/lang/en_us.json"))).getAsJsonObject(); + } +} diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/NativeItemAtlasContractTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/NativeItemAtlasContractTest.java index 9e360f2..bd295f4 100644 --- a/src/test/java/zone/moddev/mc/basesteam/resources/NativeItemAtlasContractTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/resources/NativeItemAtlasContractTest.java @@ -18,6 +18,7 @@ import net.minecraft.client.renderer.texture.atlas.SpriteSource; import net.minecraft.client.renderer.texture.atlas.SpriteSourceList; import net.minecraft.client.renderer.texture.atlas.SpriteSources; +import net.minecraft.client.renderer.texture.atlas.sources.DirectoryLister; import net.minecraft.data.AtlasIds; import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; @@ -27,6 +28,7 @@ import net.minecraft.server.packs.PathPackResources; import net.minecraft.server.packs.repository.PackSource; import net.minecraft.server.packs.resources.MultiPackResourceManager; +import net.minecraft.server.packs.resources.ResourceManager; import org.junit.BeforeClass; import org.junit.Test; import zone.moddev.mc.basesteam.testing.NativeTestBootstrap; @@ -38,7 +40,7 @@ public class NativeItemAtlasContractTest { SpriteSources.bootstrap(); } - @Test public void pinnedNativeItemAtlasFindsEveryLegacySteamModelTextureAcrossNamespaces() throws Exception { + @Test public void pinnedNativeAtlasesResolveEveryModelWithoutBlockMaterialCollisions() throws Exception { Properties pins = new Properties(); try (var input = Files.newInputStream(Path.of("gradle.properties"))) { pins.load(input); } Path power = Path.of("../../PowerAdvantage/build/libs/basePower-" @@ -47,67 +49,127 @@ public class NativeItemAtlasContractTest { .formatHex(MessageDigest.getInstance("SHA-256").digest(Files.readAllBytes(power)))); var steam = new PathPackResources.PathResourcesSupplier(Path.of("src/main/resources")); var parent = new FilePackResources.FileResourcesSupplier(power); - Identifier atlas = Identifier.withDefaultNamespace("atlases/items.json"); assertEquals(Identifier.withDefaultNamespace("items"), AtlasIds.ITEMS); + for (String directory : List.of("blocks", "materials")) { + assertThrows("Block/material directories must never be accepted by the item atlas", AssertionError.class, + () -> requireItemsOnly(List.of(new DirectoryLister(directory, directory + "/")))); + } + assertThrows("Even an items source must retain its exact sprite prefix", AssertionError.class, + () -> requireItemsOnly(List.of(new DirectoryLister("items", "")))); try (var resources = new MultiPackResourceManager(PackType.CLIENT_RESOURCES, List.of(parent.openPrimary(info("basepower")), steam.openPrimary(info("basesteam"))))) { - var definitions = resources.getResourceStack(atlas); - assertFalse("The native item atlas must be supplied by the exact Power candidate", definitions.isEmpty()); - Map found = new HashMap<>(); - SpriteSource.Output output = new SpriteSource.Output() { - @Override public void add(Identifier id, SpriteSource.DiscardableLoader loader) { - var replaced = found.put(id, loader); - if (replaced != null) replaced.discard(); - } - @Override public void removeAll(java.util.function.Predicate filter) { - found.entrySet().removeIf(entry -> { - if (!filter.test(entry.getKey())) return false; - entry.getValue().discard(); - return true; - }); - } - }; - for (var definition : definitions) { - try (var reader = definition.openAsReader()) { - var sources = SpriteSources.FILE_CODEC.parse( - new Dynamic<>(JsonOps.INSTANCE, JsonParser.parseReader(reader))).getOrThrow(); - sources.forEach(source -> source.run(resources, output)); - } - } - try { - Set expected = textures(); - assertFalse("Inspect the actual Steam model texture declarations", expected.isEmpty()); - assertTrue("The loaded musket retains its original barrel-tip material texture", - expected.contains(Identifier.parse("basesteam:materials/steel_dark_hole"))); - assertTrue("The shared Power textures must also be covered", expected.stream() - .anyMatch(id -> id.getNamespace().equals("basepower"))); - Set missing = new HashSet<>(expected); - missing.removeAll(found.keySet()); - assertTrue("Textures absent from the actual native item-atlas sources: " + missing, missing.isEmpty()); - assertFalse(found.containsKey(Identifier.parse("basesteam:missing_atlas_negative_control"))); - for (Identifier id : expected) { - var file = id.withPrefix("textures/").withSuffix(".png"); - try (var input = resources.getResourceOrThrow(file).open()) { - assertArrayEquals(id.toString(), new byte[] {(byte)137, 80, 78, 71, 13, 10, 26, 10}, input.readNBytes(8)); - } + Set blockDirectories = new HashSet<>(); + Set itemDirectories = new HashSet<>(); + Set blockSprites = sprites(resources, AtlasIds.BLOCKS, blockDirectories); + Set itemSprites = sprites(resources, AtlasIds.ITEMS, itemDirectories); + Set blockTextures = textures(Path.of("src/main/resources/assets/basesteam/models/block")); + assertFalse("The block-model texture oracle must not be empty", blockTextures.isEmpty()); + Set duplicatedDirectories = new HashSet<>(blockDirectories); + duplicatedDirectories.retainAll(itemDirectories); + duplicatedDirectories.retainAll(Set.of("blocks", "materials")); + assertTrue("ITEMS must not shadow block/material directories: " + duplicatedDirectories, + duplicatedDirectories.isEmpty()); + Set collisions = new HashSet<>(blockSprites); + collisions.retainAll(itemSprites); + collisions.retainAll(blockTextures); + assertTrue("block_or_item must not resolve world-block textures through ITEMS: " + collisions, + collisions.isEmpty()); + Set expected = textures(Path.of("src/main/resources/assets/basesteam/models")); + assertFalse("Inspect the actual Steam model texture declarations", expected.isEmpty()); + assertTrue("The loaded musket retains its original barrel-tip material texture", + expected.contains(Identifier.parse("basesteam:materials/steel_dark_hole"))); + assertTrue("The shared Power textures must also be covered", expected.stream() + .anyMatch(id -> id.getNamespace().equals("basepower"))); + Set allSprites = new HashSet<>(blockSprites); + allSprites.addAll(itemSprites); + Set missing = new HashSet<>(expected); + missing.removeAll(allSprites); + assertTrue("Textures absent from both actual native atlas sources: " + missing, missing.isEmpty()); + assertTrue("BlockSimpleModelWrapper requires the block atlas", blockSprites.containsAll(blockTextures)); + assertTrue("Block model materials must not resolve through ITEMS", + java.util.Collections.disjoint(blockTextures, itemSprites)); + Set itemIcons = expected.stream().filter(id -> id.getPath().startsWith("items/")) + .collect(java.util.stream.Collectors.toSet()); + assertFalse("The legacy generated-item icon oracle must not be empty", itemIcons.isEmpty()); + assertTrue("Native item lookup uses ITEMS", itemSprites.containsAll(itemIcons)); + assertTrue("Legacy generated-item lookup retains BLOCKS compatibility", blockSprites.containsAll(itemIcons)); + assertTrue(blockSprites.contains(Identifier.parse("basesteam:entity/drill_bit"))); + assertFalse(itemSprites.contains(Identifier.parse("basesteam:entity/drill_bit"))); + assertFalse(allSprites.contains(Identifier.parse("basesteam:missing_atlas_negative_control"))); + for (Identifier id : expected) { + var file = id.withPrefix("textures/").withSuffix(".png"); + try (var input = resources.getResourceOrThrow(file).open()) { + assertArrayEquals(id.toString(), new byte[] {(byte)137, 80, 78, 71, 13, 10, 26, 10}, input.readNBytes(8)); } - } finally { - found.values().forEach(SpriteSource.DiscardableLoader::discard); } } try (var resources = new MultiPackResourceManager(PackType.CLIENT_RESOURCES, List.of(steam.openPrimary(info("basesteam"))))) { - assertEquals("Steam contributes only its drill sprite", 1, resources.getResourceStack(atlas).size()); + assertEquals("Steam contributes one item-directory definition", 1, + resources.getResourceStack(Identifier.withDefaultNamespace("atlases/items.json")).size()); assertTrue("Shared materials cannot exist without the required Power pack", resources.getResource( Identifier.parse("basepower:textures/materials/wood.png")).isEmpty()); - assertEquals("Without Power, the native loader offers only the drill sprite and missing-texture fallback", 2, + assertEquals("Without Power, the native item loader offers three icons plus missing-texture fallback", 4, SpriteSourceList.load(resources, AtlasIds.ITEMS).list(resources).size()); + Set available = new HashSet<>(sprites(resources, AtlasIds.BLOCKS, new HashSet<>())); + available.addAll(sprites(resources, AtlasIds.ITEMS, new HashSet<>())); + Set missing = textures(Path.of("src/main/resources/assets/basesteam/models")); + missing.removeAll(available); + assertTrue("Omitting Power must still expose the missing shared material", + missing.contains(Identifier.parse("basesteam:materials/steel_dark_hole"))); + } + } + + private static Set sprites(ResourceManager resources, Identifier atlas, Set directories) + throws Exception { + var definitions = resources.getResourceStack(atlas.withPrefix("atlases/").withSuffix(".json")); + assertFalse("Missing native atlas " + atlas, definitions.isEmpty()); + Map found = new HashMap<>(); + SpriteSource.Output output = new SpriteSource.Output() { + @Override public void add(Identifier id, SpriteSource.DiscardableLoader loader) { + var replaced = found.put(id, loader); + if (replaced != null) replaced.discard(); + } + @Override public void removeAll(java.util.function.Predicate filter) { + found.entrySet().removeIf(entry -> { + if (!filter.test(entry.getKey())) return false; + entry.getValue().discard(); + return true; + }); + } + }; + try { + for (var definition : definitions) { + try (var reader = definition.openAsReader()) { + var sources = SpriteSources.FILE_CODEC.parse( + new Dynamic<>(JsonOps.INSTANCE, JsonParser.parseReader(reader))).getOrThrow(); + if (atlas.equals(AtlasIds.ITEMS)) requireItemsOnly(sources); + for (SpriteSource source : sources) { + if (source instanceof DirectoryLister directory) { + assertEquals(directory.sourcePath() + "/", directory.idPrefix()); + directories.add(directory.sourcePath()); + } + source.run(resources, output); + } + } + } + return new HashSet<>(found.keySet()); + } finally { + found.values().forEach(SpriteSource.DiscardableLoader::discard); } } - private static Set textures() throws IOException { + private static void requireItemsOnly(List sources) { + assertEquals("Only the native items directory belongs in this atlas", 1, sources.size()); + assertTrue("Single block/material sprites must not shadow the block atlas", sources.getFirst() instanceof DirectoryLister); + DirectoryLister source = (DirectoryLister)sources.getFirst(); + assertEquals("items", source.sourcePath()); + assertEquals("items/", source.idPrefix()); + } + + private static Set textures(Path root) throws IOException { Set result = new HashSet<>(); - try (var files = Files.walk(Path.of("src/main/resources/assets/basesteam/models"))) { + try (var files = Files.walk(root)) { for (Path file : files.filter(path -> path.toString().endsWith(".json")).toList()) { var model = JsonParser.parseString(Files.readString(file)).getAsJsonObject(); if (!model.has("textures")) continue; From 309249d48d737555bd27e15d81d927fe405a47f2 Mon Sep 17 00:00:00 2001 From: JohnBraham Date: Wed, 11 Nov 2026 19:23:39 +0000 Subject: [PATCH 5/8] Pin verified Power D biome-index integration --- gradle.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle.properties b/gradle.properties index debc58c..6c212b2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -43,11 +43,11 @@ cf_versions=1.21.11 poweradvantage_version=2.4.15.121111 poweradvantage_source_repository=MinecraftModDevelopmentMods/basePower # Exact approved LOCAL integration candidate; no public 1.21.11 release is implied. -poweradvantage_source_ref=a1cd197b4b6c3fc9c5d56da254d0f0ae9929fe8c +poweradvantage_source_ref=3eedaeb19039c0bd16437e8a7025cd20c31f2eac poweradvantage_public_file_id= -poweradvantage_deobf_sha256=134FF55595E86D450DBFB5531F389BF0BE81FE6347F1EB03765B571A1D488B59 +poweradvantage_deobf_sha256=3EAE894F8F305676AEACF85E96A7497ACD7104CC953DD87AEB951F8567EA25DB # Forge 61 uses the same native jar for development and packaged runtime. -poweradvantage_player_sha256=134FF55595E86D450DBFB5531F389BF0BE81FE6347F1EB03765B571A1D488B59 +poweradvantage_player_sha256=3EAE894F8F305676AEACF85E96A7497ACD7104CC953DD87AEB951F8567EA25DB poweradvantage_test_bootstrap_sha256=FDE28801E41787E05212F3DEF7FAF34C6859402A22B71866D15B468C300DA883 poweradvantage_test_bootstrap_source_sha256=CDE40D09A40DB95F293BC56723E70B321C205850275696BEBFC2A575A8B7FE48 poweradvantage_test_registry_source_sha256=01DDE50C9874C2DC501BE2F699CD8D16D6974FACFAAC1B803434CA8C64227ABF From 1353824b05c71b44108552ca74f6752e94f4b2ee Mon Sep 17 00:00:00 2001 From: JohnBraham Date: Wed, 11 Nov 2026 20:56:37 +0000 Subject: [PATCH 6/8] Require Base Metals in native metadata and pin approved Power --- CHANGELOG.txt | 1 + README.md | 8 +- build.gradle | 14 ++- docs/PORT12111-STATUS.md | 31 +++++- gradle.properties | 6 +- src/main/resources/META-INF/mods.toml | 2 +- .../resources/BuildWorkflowContractTest.java | 13 +++ .../NativeRequiredDependencyContractTest.java | 105 ++++++++++++++++++ tools/run-packaged-server-smoke.ps1 | 7 +- tools/verify-native-port.js | 2 +- 10 files changed, 175 insertions(+), 14 deletions(-) create mode 100644 src/test/java/zone/moddev/mc/basesteam/resources/NativeRequiredDependencyContractTest.java diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 5575b47..728905d 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -4,6 +4,7 @@ baseSteam 2.2.16.121111 (local Minecraft 1.21.11 source port) - Adapt native registration, persistence, recipes, item definitions and drill rendering. - Retain registry identities, legacy state keys, recipe payloads and configuration defaults. - Use native Forge hidden-client pack metadata with data-format 94.1 at the mod root. +- Require Base Metals on both client and server in Forge metadata as well as distribution metadata; retain the defensive recipe guards unchanged. - Keep block/material sprites, including the drill bit, out of the native item atlas; retain legacy item-icon lookup compatibility. - Local source, archive, compiler, GameTest and packaged reopen gates pass; combined world upgrades, endurance and manual acceptance are separate. diff --git a/README.md b/README.md index 85ae7d5..5c69fc5 100644 --- a/README.md +++ b/README.md @@ -16,10 +16,15 @@ also needs Java 8. Minecraft itself runs on Java 21. The default dependency paths use the coordinated 1.21.11 checkouts. This candidate needs basePower 2.4.15.121111, Base Metals 3.1.2.121111 and OreSpawn 4.1.0.121111 as separate native jars, not bundled copies. +Base Metals is REQUIRED on both client and server, not an optional integration. Exact local source, jar and test-bootstrap pins are in `gradle.properties`. They are checked before compilation and tests. Local approval does not establish public availability. The Base Metals shared hook and approved Power -integration commit a1cd197b4b6c3fc9c5d56da254d0f0ae9929fe8c are pinned locally. +integration commit 41959572b11dce236df98f23e97c6a3868cdda03 are pinned locally. +The required-dependency metadata correction is local only. Power's approved +metadata rebuild is pinned, but the changed Steam bytes are not +a qualified family release. Earlier no-Base-Metals receipts are +historical evidence, not a supported installation profile. The mod root declares server data format 94.1. Forge supplies format 75.x metadata for its hidden client resource aggregate. Native resource-reader tests cover both paths without codec warnings; see @@ -46,7 +51,6 @@ $toolchains = @("-Porg.gradle.java.installations.paths=$jdks", ./gradlew.bat @toolchains verifyEclipseProductionClasspath ./gradlew.bat @toolchains -PsteamGameTestRunId=candidate verifyPlayerDependencies runGameTestServer ./tools/run-packaged-server-smoke.ps1 -ForgeInstall $env:FORGE_12111_SERVER_INSTALL -RunId candidate-with-metals -UseNioTransport -Reopen -./tools/run-packaged-server-smoke.ps1 -ForgeInstall $env:FORGE_12111_SERVER_INSTALL -RunId candidate-without-metals -WithoutBaseMetals -UseNioTransport -Reopen ``` Independent source checks: `node tools/verify-native-port.js` and diff --git a/build.gradle b/build.gradle index 16df66d..4147f1e 100644 --- a/build.gradle +++ b/build.gradle @@ -461,6 +461,15 @@ def verifyPackFormats = { pack -> } } +def verifyRequiredBaseMetalsMetadata = { String metadata -> + String normalized = metadata.replace('\r\n', '\n') + String declaration = 'modId="basemetals"\nmandatory=true\nversionRange="[3.1.2.121111,4.0.0)"\nordering="AFTER"\nside="BOTH"' + if (!normalized.contains(declaration) + || normalized.readLines().count { it.trim() == 'modId="basemetals"' } != 1) { + throw new GradleException('Base Metals must be declared exactly once as REQUIRED on BOTH sides, ordered AFTER, in [3.1.2.121111,4.0.0)') + } +} + tasks.register('verifyReleaseConfiguration') { group = 'verification' description = 'Validates release identity, target metadata, and dependency policy.' @@ -519,12 +528,12 @@ tasks.register('verifyReleaseConfiguration') { } String metadata = file('src/main/resources/META-INF/mods.toml').getText('UTF-8').replace('\r\n', '\n') ['showAsResourcePack=false', 'modId="basesteam"', 'displayName="baseSteam"', - 'modId="basepower"\nmandatory=true\nversionRange="[${poweradvantage_version},3.0.0)"', - 'modId="basemetals"\nmandatory=false\nversionRange="[3.1.2.121111,4.0.0)"'].each { marker -> + 'modId="basepower"\nmandatory=true\nversionRange="[${poweradvantage_version},3.0.0)"'].each { marker -> if (!metadata.contains(marker)) { throw new GradleException("Source mods.toml is missing ${marker}") } } + verifyRequiredBaseMetalsMetadata(metadata) } } @@ -782,6 +791,7 @@ tasks.register('verifyReleaseArtifacts') { } String metadata = zip.getInputStream(zip.getEntry('META-INF/mods.toml')) .getText(StandardCharsets.UTF_8.name()) + verifyRequiredBaseMetalsMetadata(metadata) if (!metadata.contains('modId="basesteam"') || !metadata.contains('showAsResourcePack=false') || !metadata.contains("version=\"${project.mod_version}\"") diff --git a/docs/PORT12111-STATUS.md b/docs/PORT12111-STATUS.md index 83294b5..fb49a9a 100644 --- a/docs/PORT12111-STATUS.md +++ b/docs/PORT12111-STATUS.md @@ -1,5 +1,30 @@ # Steam on Minecraft 1.21.11 +## Current Metadata Correction + +Base Metals is REQUIRED on both client and server. The local mods.toml +declares it exactly once with mandatory=true, ordering AFTER and the unchanged +version range [3.1.2.121111,4.0.0). Distribution metadata already required it. +The existing recipe conditions remain defensive data guards; they do not make +a no-Base-Metals installation supported. + +This correction changes player-jar metadata bytes, not version, production +classes, recipes, configuration or dependency pins. The previous 25EE player +jar is held under build/required-basemetals-41/held-before, with its checksum +recorded before editing. Focused native ModInfo regression/static checks and +any local candidate-byte report are recorded under build/required-basemetals-41. +These local edits are not a frozen release or whole-world qualification. + +Power's approved metadata source 41959572b11dce236df98f23e97c6a3868cdda03 +and SHA-256 C98D2CF163BE2480D9AFB0023BBD2D92D401AE0D50BEF73191235BF3F83774A7 +are now pinned for the consistent default build. The RED attempt used the +preserved, hash-checked D input without changing its committed pins at that +stage. No OreSpawn change, publication, version bump or packaged-world/endurance +campaign is part of this correction. The smoke helper rejects its former +no-Base-Metals mode before starting a runtime. + +## Historical C2 Proof + This is historical C2 candidate qualification against Power134F, not an overall family release claim. The parent reports a later, broader chained 1.13 dimension route exposing a Power dynamic-biome-registry issue; its fix @@ -78,8 +103,10 @@ All four native GameTests pass, selected by basesteam:* with exact named XML results and no failures/skips. Forge 61's test-function registry and native test_instance resources are confined to the isolated GameTest source scope. The first launch's built-in-only one-test result correctly failed the gate. -Packaged dedicated startup/save/reopen passes in two separate JVMs for both -the with-Base-Metals and without-Base-Metals profiles. Every process verifies +Historical pre-clarification dedicated startup/save/reopen evidence exists +for both the with-Base-Metals and without-Base-Metals inputs. The latter is +retained only as old evidence: it is not supported by the current REQUIRED +dependency policy and does not qualify the new metadata bytes. Every old process verifies the exact copied candidate hashes and checks latest/stdout/stderr without filtering loader/data errors. These profiles explicitly select native NIO transport on Windows. The earlier default native-availability selection failed diff --git a/gradle.properties b/gradle.properties index 6c212b2..6a802ac 100644 --- a/gradle.properties +++ b/gradle.properties @@ -43,11 +43,11 @@ cf_versions=1.21.11 poweradvantage_version=2.4.15.121111 poweradvantage_source_repository=MinecraftModDevelopmentMods/basePower # Exact approved LOCAL integration candidate; no public 1.21.11 release is implied. -poweradvantage_source_ref=3eedaeb19039c0bd16437e8a7025cd20c31f2eac +poweradvantage_source_ref=41959572b11dce236df98f23e97c6a3868cdda03 poweradvantage_public_file_id= -poweradvantage_deobf_sha256=3EAE894F8F305676AEACF85E96A7497ACD7104CC953DD87AEB951F8567EA25DB +poweradvantage_deobf_sha256=C98D2CF163BE2480D9AFB0023BBD2D92D401AE0D50BEF73191235BF3F83774A7 # Forge 61 uses the same native jar for development and packaged runtime. -poweradvantage_player_sha256=3EAE894F8F305676AEACF85E96A7497ACD7104CC953DD87AEB951F8567EA25DB +poweradvantage_player_sha256=C98D2CF163BE2480D9AFB0023BBD2D92D401AE0D50BEF73191235BF3F83774A7 poweradvantage_test_bootstrap_sha256=FDE28801E41787E05212F3DEF7FAF34C6859402A22B71866D15B468C300DA883 poweradvantage_test_bootstrap_source_sha256=CDE40D09A40DB95F293BC56723E70B321C205850275696BEBFC2A575A8B7FE48 poweradvantage_test_registry_source_sha256=01DDE50C9874C2DC501BE2F699CD8D16D6974FACFAAC1B803434CA8C64227ABF diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 6c44f9b..f426bd1 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -21,7 +21,7 @@ side="BOTH" [[dependencies.basesteam]] modId="basemetals" -mandatory=false +mandatory=true versionRange="[3.1.2.121111,4.0.0)" ordering="AFTER" side="BOTH" diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/BuildWorkflowContractTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/BuildWorkflowContractTest.java index dc47669..927798a 100644 --- a/src/test/java/zone/moddev/mc/basesteam/resources/BuildWorkflowContractTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/resources/BuildWorkflowContractTest.java @@ -52,4 +52,17 @@ public class BuildWorkflowContractTest { assertTrue(script.contains("$(value orespawn_public_file_id)")); assertTrue(script.contains("sha256sum -c -")); } + + @Test public void sourceAndPackageAuditsEnforceRequiredBaseMetalsWithoutAnOptionalLaunchScope() throws Exception { + String build = Files.readString(Path.of("build.gradle")); + assertEquals(2, build.lines().filter(line -> line.contains("verifyRequiredBaseMetalsMetadata(metadata)")).count()); + assertTrue(build.contains("Base Metals must be declared exactly once as REQUIRED on BOTH sides")); + String readme = Files.readString(Path.of("README.md")); + assertTrue(readme.contains("Base Metals is REQUIRED")); + assertFalse(readme.contains("-WithoutBaseMetals")); + String smoke = Files.readString(Path.of("tools/run-packaged-server-smoke.ps1")); + int reject = smoke.indexOf("if ($WithoutBaseMetals)"); + assertTrue(reject >= 0 && reject < smoke.indexOf("$project =")); + assertTrue(smoke.contains("a no-Base-Metals runtime is not supported")); + } } diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/NativeRequiredDependencyContractTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/NativeRequiredDependencyContractTest.java new file mode 100644 index 0000000..d9cae1d --- /dev/null +++ b/src/test/java/zone/moddev/mc/basesteam/resources/NativeRequiredDependencyContractTest.java @@ -0,0 +1,105 @@ +package zone.moddev.mc.basesteam.resources; + +import cpw.mods.jarhandling.SecureJar; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import net.minecraftforge.fml.loading.moddiscovery.ModFile; +import net.minecraftforge.fml.loading.moddiscovery.ModFileParser; +import net.minecraftforge.fml.loading.moddiscovery.ModInfo; +import net.minecraftforge.fml.loading.targets.ForgeDevLocator; +import net.minecraftforge.forgespi.language.IModInfo; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.junit.Test; +import static org.junit.Assert.*; + +public class NativeRequiredDependencyContractTest { + @Test public void processedMetadataRequiresBaseMetalsThroughActualForgeModInfo() throws Exception { + requireBaseMetals(parse(Files.readString(Path.of("build/resources/main/META-INF/mods.toml")))); + } + + @Test public void nativeParserDistinguishesRequiredAndOptionalDependencyFlags() throws Exception { + assertFalse(baseMetals(parse(fixture(dependency(false)))).isMandatory()); + assertTrue(baseMetals(parse(fixture(dependency(true)))).isMandatory()); + } + + @Test public void requiredContractRejectsOptionalMissingAndDuplicateDependencies() throws Exception { + for (String declarations : new String[] {dependency(false), "", dependency(true) + dependency(true)}) { + ModInfo parsed = parse(fixture(declarations)); + assertThrows("Missing, optional or duplicated Base Metals cannot satisfy the contract", + AssertionError.class, () -> requireBaseMetals(parsed)); + } + } + + public static void main(String[] args) throws Exception { + if (args.length != 1) throw new IllegalArgumentException("Expected the actual candidate jar path"); + requireBaseMetals(parseJar(Path.of(args[0]))); + System.out.println("NATIVE_REQUIRED_BASEMETALS_ARTIFACT_PASS"); + } + + private static void requireBaseMetals(ModInfo info) { + IModInfo.ModVersion dependency = baseMetals(info); + assertTrue("Base Metals is REQUIRED, not an optional installation scope", dependency.isMandatory()); + assertEquals(IModInfo.Ordering.AFTER, dependency.getOrdering()); + assertEquals(IModInfo.DependencySide.BOTH, dependency.getSide()); + assertEquals("[3.1.2.121111,4.0.0)", dependency.getVersionRange().toString()); + assertTrue(dependency.getVersionRange().containsVersion(new DefaultArtifactVersion("3.1.2.121111"))); + assertFalse(dependency.getVersionRange().containsVersion(new DefaultArtifactVersion("3.1.1.121111"))); + assertFalse(dependency.getVersionRange().containsVersion(new DefaultArtifactVersion("4.0.0"))); + } + + private static IModInfo.ModVersion baseMetals(ModInfo info) { + var dependencies = info.getDependencies().stream().filter(item -> item.getModId().equals("basemetals")).toList(); + assertEquals("Declare exactly one Base Metals dependency", 1, dependencies.size()); + return dependencies.getFirst(); + } + + private static ModInfo parse(String metadata) throws Exception { + Path root = Path.of("build/native-required-dependency-metadata"); + Files.createDirectories(root); + Path jar = Files.createTempDirectory(root, "forge61-").resolve("metadata.jar"); + Manifest manifest = new Manifest(); + manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); + manifest.getMainAttributes().putValue("Automatic-Module-Name", "basesteam"); + try (JarOutputStream output = new JarOutputStream(Files.newOutputStream(jar), manifest)) { + output.putNextEntry(new JarEntry("META-INF/mods.toml")); + output.write(metadata.getBytes(StandardCharsets.UTF_8)); + output.closeEntry(); + } + return parseJar(jar); + } + + private static ModInfo parseJar(Path jar) { + ModFile mod = new ModFile(SecureJar.from(jar), new ForgeDevLocator(), ModFileParser::modsTomlParser); + assertEquals(1, mod.getModInfos().size()); + assertEquals("basesteam", mod.getModInfos().getFirst().getModId()); + return (ModInfo)mod.getModInfos().getFirst(); + } + + private static String fixture(String dependency) { + return """ + modLoader="javafml" + loaderVersion="[61,62)" + license="GPL-3.0-only" + [[mods]] + modId="basesteam" + version="2.2.16.121111" + displayName="baseSteam" + """ + dependency; + } + + private static String dependency(boolean required) { + return """ + [[dependencies.basesteam]] + modId="basemetals" + mandatory=%s + versionRange="[3.1.2.121111,4.0.0)" + ordering="AFTER" + side="BOTH" + """.formatted(required); + } +} diff --git a/tools/run-packaged-server-smoke.ps1 b/tools/run-packaged-server-smoke.ps1 index 3015f16..a9d6720 100644 --- a/tools/run-packaged-server-smoke.ps1 +++ b/tools/run-packaged-server-smoke.ps1 @@ -12,6 +12,9 @@ param( ) $ErrorActionPreference = 'Stop' +if ($WithoutBaseMetals) { + throw 'Base Metals is required by baseSteam; a no-Base-Metals runtime is not supported.' +} $project = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..')).Path $forge = (Resolve-Path -LiteralPath $ForgeInstall).Path $pins = Get-Content -Raw -LiteralPath (Join-Path $project 'gradle.properties') | ConvertFrom-StringData @@ -46,9 +49,7 @@ $mods = [ordered]@{ "basePower-$($pins.poweradvantage_version).jar" = @{ Path = $PowerJar; Sha256 = $pins.poweradvantage_player_sha256 } "OreSpawn-$($pins.orespawn_version).jar" = @{ Path = $OreSpawnJar; Sha256 = $pins.orespawn_player_sha256 } } -if (!$WithoutBaseMetals) { - $mods["BaseMetals-$($pins.basemetals_version).jar"] = @{ Path = $BaseMetalsJar; Sha256 = $pins.basemetals_player_sha256 } -} +$mods["BaseMetals-$($pins.basemetals_version).jar"] = @{ Path = $BaseMetalsJar; Sha256 = $pins.basemetals_player_sha256 } $forgeVersion = "$($pins.minecraft_version)-$($pins.forge_version)" $shim = "forge-$forgeVersion-shim.jar" $forgeLib = "libraries/net/minecraftforge/forge/$forgeVersion" diff --git a/tools/verify-native-port.js b/tools/verify-native-port.js index d5f1c30..8d53439 100644 --- a/tools/verify-native-port.js +++ b/tools/verify-native-port.js @@ -31,7 +31,7 @@ const metadata = read(`${resources}/META-INF/mods.toml`); assert.match(metadata, /^showAsResourcePack=false$/m); assert.match(metadata, /modId="basesteam"/); assert.match(metadata, /modId="basepower"\r?\nmandatory=true\r?\nversionRange="\[\$\{poweradvantage_version\},3\.0\.0\)"/); -assert.match(metadata, /modId="basemetals"\r?\nmandatory=false/); +assert.match(metadata, /modId="basemetals"\r?\nmandatory=true/); assert.doesNotMatch(metadata, /dependencies\.steamadvantage|modId="(?:steamadvantage|poweradvantage)"/); function verifyLocalAsset(id, directory, extension, name) { From 3ea780f24a7a2c778253ee8f9d756aabf18e85a9 Mon Sep 17 00:00:00 2001 From: JohnBraham Date: Wed, 11 Nov 2026 23:40:28 +0000 Subject: [PATCH 7/8] Prepare Steam release notes and portable tests --- CHANGELOG.txt | 7 ++- README.md | 37 +++++++++--- build.gradle | 1 + docs/PORT12111-STATUS.md | 57 ++++++++++++++++++- .../NativeItemAtlasContractTest.java | 32 +++++++++-- .../testing/NativePackResourceProof.java | 21 +++++-- .../testing/NativePackResourceProofTest.java | 38 +++++++++++++ 7 files changed, 174 insertions(+), 19 deletions(-) create mode 100644 src/test/java/zone/moddev/mc/basesteam/testing/NativePackResourceProofTest.java diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 728905d..144804e 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,4 +1,4 @@ -baseSteam 2.2.16.121111 (local Minecraft 1.21.11 source port) +baseSteam 2.2.16.121111 (Minecraft 1.21.11 / Forge, accepted for release) - Target Forge 61.1.0, Java 21 and the pinned local basePower 2.4.15.121111 API. - Adapt native registration, persistence, recipes, item definitions and drill rendering. @@ -6,7 +6,10 @@ baseSteam 2.2.16.121111 (local Minecraft 1.21.11 source port) - Use native Forge hidden-client pack metadata with data-format 94.1 at the mod root. - Require Base Metals on both client and server in Forge metadata as well as distribution metadata; retain the defensive recipe guards unchanged. - Keep block/material sprites, including the drill bit, out of the native item atlas; retain legacy item-icon lookup compatibility. -- Local source, archive, compiler, GameTest and packaged reopen gates pass; combined world upgrades, endurance and manual acceptance are separate. +- Make the atlas regression test use the configured, checksum-verified Power jar, including builds outside the usual sibling folders. +- Keep native resource tests noninteractive in CI without suppressing warnings or errors. +- Complete the combined machine, upgrade, graphical and two-world endurance checks against the current jars. +- Accepted for release; dependency publication and hosted release checks are still pending. baseSteam 2.2.15.121011 (Minecraft 1.21.1 / Forge) diff --git a/README.md b/README.md index 5c69fc5..dcee80c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # baseSteam -**Minecraft 1.21.11 (Forge), local source port 2.2.16.121111.** +**Minecraft 1.21.11 (Forge), baseSteam 2.2.16.121111.** baseSteam adds boilers, steam-powered machines, pumps, storage, elevators and a blackpowder musket to basePower. Items and blocks share the basePower @@ -21,9 +21,12 @@ Exact local source, jar and test-bootstrap pins are in `gradle.properties`. They are checked before compilation and tests. Local approval does not establish public availability. The Base Metals shared hook and approved Power integration commit 41959572b11dce236df98f23e97c6a3868cdda03 are pinned locally. -The required-dependency metadata correction is local only. Power's approved -metadata rebuild is pinned, but the changed Steam bytes are not -a qualified family release. Earlier no-Base-Metals receipts are +The 1.21.11 port is accepted for release. The current family has passed the +combined machine, upgrade, graphical and two-world 30-minute endurance checks. +Publication is still pending the dependency releases and hosted checks; these +local pins have not been replaced with released inputs. The full record is +in basePower's +`docs/1.21.11/QUALIFICATION.md`. Earlier no-Base-Metals receipts are historical evidence, not a supported installation profile. The mod root declares server data format 94.1. Forge supplies format 75.x metadata for its hidden client resource aggregate. Native resource-reader @@ -41,7 +44,6 @@ With the pinned local dependencies available: ```powershell $env:JAVA_HOME = "$env:USERPROFILE/.jdks/temurin-21.0.7+6" $env:GRADLE_USER_HOME = 'D:/GradleCaches/basesteam-12111-port' -$env:JAVA_TOOL_OPTIONS = '-XX:ActiveProcessorCount=4' $jdk8 = "$env:USERPROFILE/.jdks/temurin-8.0.502+7/jdk8u502-b07" $jdks = "$env:JAVA_HOME,$env:USERPROFILE/.jdks/jdk-25.0.3+9,$jdk8" $toolchains = @("-Porg.gradle.java.installations.paths=$jdks", @@ -53,6 +55,24 @@ $toolchains = @("-Porg.gradle.java.installations.paths=$jdks", ./tools/run-packaged-server-smoke.ps1 -ForgeInstall $env:FORGE_12111_SERVER_INSTALL -RunId candidate-with-metals -UseNioTransport -Reopen ``` +For a checkout outside the sibling-folder layout, unit tests also need the +verified Power test bootstrap and its two source files. Build Power first, +then supply these paths as well as the dependency-jar overrides: + +```text +-PforgeCapabilityBootstrapJar=/build/unit-bootstrap/forge-native-unit-bootstrap.jar +-PforgeCapabilityBootstrapSource=/test-rig/unit-bootstrap/ForgeCapabilityBootstrap.java +-PforgeUnitRegistriesSource=/test-rig/unit-support/zone/moddev/mc/advantageworks/unit/ForgeUnitRegistries.java +``` + +The atlas test uses the same verified Power jar selected for compilation; +it does not look for a second copy in a fixed local folder. Missing or +changed inputs fail the checks. The current source check passes 252 unit +tests. Native resource tests disable interactive terminal setup in their +child process, while keeping warnings and errors fatal. +Keep Java option environment variables unset +when running the packaged family rig; it records its own runtime arguments. + Independent source checks: `node tools/verify-native-port.js` and `node tools/generate-recipe-unlocks.js --check`. Hosted CI and CodeQL require merged sources and checksum-matching public @@ -68,10 +88,11 @@ Steam retains machine IDs and saved fields, drill directions, musket wear and loading, and all four musket enchantments. The singular datapack folders do not change logical recipe or advancement identities. -Use basePower's migration rig for whole-world checks. Before accepting the -port, check machines, restarts, redstone, all drill directions, elevators, +Use basePower's migration rig for whole-world checks. For your own world, +check machines, restarts, redstone, all drill directions, elevators, GUI captions, whole fluid gauges, track transparency and musket effects -in-game. Automated, packaged and manual acceptance are separate gates. +in-game. Release acceptance does not turn the remaining manual checks into +automated passes or qualify every modpack. See the [1.21.11 notes](docs/PORT12111-STATUS.md). The [1.21.1 notes](docs/PORT1211-STATUS.md) describe the accepted source baseline, not qualification of this port. diff --git a/build.gradle b/build.gradle index 4147f1e..cbba5bf 100644 --- a/build.gradle +++ b/build.gradle @@ -340,6 +340,7 @@ tasks.named('test', Test) { } logger.lifecycle("basePower test bootstrap: ${bootstrapJar.canonicalPath} SHA-256 ${actualHash}") systemProperty 'basesteam.nativePackClasspath', sourceSets.test.runtimeClasspath.asPath + systemProperty 'basesteam.nativePowerJar', powerAdvantageDeobfJar.canonicalPath jvmArgs "-javaagent:${bootstrapJar.canonicalPath}" jvmArgs '--add-opens', 'java.base/java.lang.invoke=ALL-UNNAMED' } diff --git a/docs/PORT12111-STATUS.md b/docs/PORT12111-STATUS.md index fb49a9a..5eaeb1f 100644 --- a/docs/PORT12111-STATUS.md +++ b/docs/PORT12111-STATUS.md @@ -1,6 +1,61 @@ # Steam on Minecraft 1.21.11 -## Current Metadata Correction +## Accepted For Release + +The current `F23DDC4ABB36A1C4C7A4205151FBB6DDFD25397E86909E1C0B146C4089B747E0` +player jar was accepted for release on 11 November 2026 with Power C98D, +Electric 5466, Base Metals 598D and OreSpawn DE39. Publication was authorized +in dependency order, but has not happened yet. The local dependency pins are +unchanged pending the released dependency source, jar hash and public file ID. +basePower's `docs/1.21.11/QUALIFICATION.md` records the full hashes and limits. + +The current frozen family passes 132 finite jobs, upgrades, chunk/network and +drill lifecycle tests, recipes, world generation, oils, graphical checks and +two 30-minute endurance worlds with full restarts. Independent replay passes +all 66 checkpoints and exact final stopped machine data. These are new +current-jar results; the C2 evidence below remains historical. + +The current forced, uncached source check passes all 252 unit tests with no +failures, errors or skips. This includes the portable Power atlas-test input +controls and two noninteractive native resource-test regressions. The child +uses terminal.jline=false with pipes and TERM=dumb, verifies 161 client and +91 server files, and rejects WARN, ERROR and FATAL diagnostics unchanged. +Its failing pre-fix receipt is preserved. + +The earlier clean-path and normal builds, and the fresh real Eclipse compiler +run, passed all 250 tests before those two terminal regressions were added. +Those receipts are not relabelled as 252-test runs. All three jars reproduce +exactly and four fresh native source GameTests pass. No Steam production +change was needed during this rerun. Connected-player interaction +and live-world visual limits remain recorded; acceptance does not relabel +unrun checks as passes. + +## Publication Preparation + +The following is the preparation status on 11 November 2026. + +The local Power source pin remains 41959572b11dce236df98f23e97c6a3868cdda03; +the Base Metals shared-hook pin remains 616d527e50e4bf0137eeb9c44fa586e34a0452a0. +Their public file IDs remain empty. CI and CodeQL deliberately require merged +upstream source and checksum-matching public jars before compilation. Those +gates must stay closed until the dependency releases are verified. + +The upstream master-1.21.11 branch still points at merged 1.21.1 tip +b145f8d1a7c2a148f8f259faec7b203cf146b6b5, which is an ancestor of this port. +There is no hosted check for the local Steam HEAD or 2.2.16.121111 tag yet. +The current CI upload list names the correct player, sources and Javadoc +jars. Its out-of-layout build supplies the Power bootstrap and both audited +sources, and the atlas test now uses that same verified dependency jar. + +The default-branch publisher needs the 1.21.11 Java 21 route and a sealed +release-registry entry. Only the route is prepared locally; the entry must +wait for the merged source and its successful CI artifact. The tag validator +checks the build job, not CodeQL or wrapper validation. No effective upstream +branch rules were returned by the audit, so all three successful checks must +also be verified before release. No tag, dispatch or publication is part of +this preparation. + +## Dependency Metadata Correction History Base Metals is REQUIRED on both client and server. The local mods.toml declares it exactly once with mandatory=true, ordering AFTER and the unchanged diff --git a/src/test/java/zone/moddev/mc/basesteam/resources/NativeItemAtlasContractTest.java b/src/test/java/zone/moddev/mc/basesteam/resources/NativeItemAtlasContractTest.java index bd295f4..94bc16e 100644 --- a/src/test/java/zone/moddev/mc/basesteam/resources/NativeItemAtlasContractTest.java +++ b/src/test/java/zone/moddev/mc/basesteam/resources/NativeItemAtlasContractTest.java @@ -43,10 +43,7 @@ public class NativeItemAtlasContractTest { @Test public void pinnedNativeAtlasesResolveEveryModelWithoutBlockMaterialCollisions() throws Exception { Properties pins = new Properties(); try (var input = Files.newInputStream(Path.of("gradle.properties"))) { pins.load(input); } - Path power = Path.of("../../PowerAdvantage/build/libs/basePower-" - + pins.getProperty("poweradvantage_version") + ".jar"); - assertEquals(pins.getProperty("poweradvantage_deobf_sha256"), HexFormat.of().withUpperCase() - .formatHex(MessageDigest.getInstance("SHA-256").digest(Files.readAllBytes(power)))); + Path power = pinnedPower(pins, System.getProperty("basesteam.nativePowerJar")); var steam = new PathPackResources.PathResourcesSupplier(Path.of("src/main/resources")); var parent = new FilePackResources.FileResourcesSupplier(power); assertEquals(Identifier.withDefaultNamespace("items"), AtlasIds.ITEMS); @@ -120,6 +117,33 @@ public class NativeItemAtlasContractTest { } } + @Test public void missingVerifiedPowerPathIsRejected() { + assertThrows(AssertionError.class, () -> pinnedPower(new Properties(), null)); + } + + @Test public void wrongPowerBytesAreRejectedEvenInAPathWithSpaces() throws Exception { + Path directory = Files.createTempDirectory("baseSteam dependency with spaces "); + Path wrong = directory.resolve("basePower.jar"); + try { + Files.write(wrong, new byte[] {1, 2, 3}); + Properties pins = new Properties(); + pins.setProperty("poweradvantage_deobf_sha256", "0".repeat(64)); + assertThrows(AssertionError.class, () -> pinnedPower(pins, wrong.toString())); + } finally { + Files.deleteIfExists(wrong); + Files.deleteIfExists(directory); + } + } + + private static Path pinnedPower(Properties pins, String configured) throws Exception { + assertNotNull("Gradle must supply its verified Power dependency to the atlas test", configured); + Path power = Path.of(configured).toAbsolutePath().normalize(); + assertTrue("Missing verified Power dependency: " + power, Files.isRegularFile(power)); + assertEquals(pins.getProperty("poweradvantage_deobf_sha256"), HexFormat.of().withUpperCase() + .formatHex(MessageDigest.getInstance("SHA-256").digest(Files.readAllBytes(power)))); + return power; + } + private static Set sprites(ResourceManager resources, Identifier atlas, Set directories) throws Exception { var definitions = resources.getResourceStack(atlas.withPrefix("atlases/").withSuffix(".json")); diff --git a/src/test/java/zone/moddev/mc/basesteam/testing/NativePackResourceProof.java b/src/test/java/zone/moddev/mc/basesteam/testing/NativePackResourceProof.java index 1293c69..98d5909 100644 --- a/src/test/java/zone/moddev/mc/basesteam/testing/NativePackResourceProof.java +++ b/src/test/java/zone/moddev/mc/basesteam/testing/NativePackResourceProof.java @@ -55,23 +55,30 @@ public static synchronized JsonObject read() throws Exception { Path javaExecutable = Path.of(System.getProperty("java.home"), "bin", "java"); if (!Files.isExecutable(javaExecutable)) javaExecutable = javaExecutable.resolveSibling("java.exe"); Path log = evidence.resolve("forge-resources.log"); - Process process = new ProcessBuilder(javaExecutable.toString(), "-XX:ActiveProcessorCount=4", "-Xmx512m", + ProcessBuilder builder = new ProcessBuilder(javaExecutable.toString(), "-XX:ActiveProcessorCount=4", "-Xmx512m", + "-Dterminal.jline=false", "--add-opens", "java.base/java.lang.invoke=ALL-UNNAMED", "-cp", classpath, NativePackResourceProof.class.getName(), Path.of("build/resources/main").toAbsolutePath().toString(), evidence.toString()).directory(evidence.toFile()) - .redirectErrorStream(true).redirectOutput(log.toFile()).start(); + .redirectInput(ProcessBuilder.Redirect.PIPE).redirectErrorStream(true).redirectOutput(log.toFile()); + builder.environment().put("TERM", "dumb"); + Process process = builder.start(); if (!process.waitFor(90, TimeUnit.SECONDS)) { process.destroyForcibly().waitFor(); throw new AssertionError("Native Forge resource proof timed out: " + log); } String output = Files.readString(log); if (process.exitValue() != 0) throw new AssertionError("Native Forge resource proof failed: " + log + "\n" + output); + requireCleanOutput(output, log); + receipt = JsonParser.parseString(Files.readString(evidence.resolve("receipt.json"))).getAsJsonObject(); + return receipt.deepCopy(); + } + + static void requireCleanOutput(String output, Path log) { if (java.util.regex.Pattern.compile("(?m)(?:\\[[^\\r\\n]*\\b(?:WARN|ERROR|FATAL)\\b[^\\r\\n]*\\]|\\bmain (?:WARN|ERROR|FATAL)\\b)") .matcher(output).find()) { throw new AssertionError("Native Forge resource diagnostic in " + log + "\n" + output); } - receipt = JsonParser.parseString(Files.readString(evidence.resolve("receipt.json"))).getAsJsonObject(); - return receipt.deepCopy(); } public static void main(String[] args) throws Exception { @@ -105,6 +112,12 @@ public static void main(String[] args) throws Exception { var logger = (org.apache.logging.log4j.core.Logger)LogManager.getRootLogger(); logger.addAppender(appender); JsonObject result = new JsonObject(); + JsonObject child = new JsonObject(); + child.addProperty("terminalJline", System.getProperty("terminal.jline")); + child.addProperty("term", System.getenv("TERM")); + child.addProperty("consoleAttached", System.console() != null); + child.addProperty("terminalCreated", net.minecrell.terminalconsole.TerminalConsoleAppender.getTerminal() != null); + result.add("childProcess", child); try { for (PackType type : PackType.values()) { PackRepository repository = new PackRepository(); diff --git a/src/test/java/zone/moddev/mc/basesteam/testing/NativePackResourceProofTest.java b/src/test/java/zone/moddev/mc/basesteam/testing/NativePackResourceProofTest.java new file mode 100644 index 0000000..429eb24 --- /dev/null +++ b/src/test/java/zone/moddev/mc/basesteam/testing/NativePackResourceProofTest.java @@ -0,0 +1,38 @@ +package zone.moddev.mc.basesteam.testing; + +import java.nio.file.Path; +import net.minecraft.server.packs.PackType; +import org.junit.Test; +import static org.junit.Assert.*; + +public class NativePackResourceProofTest { + @Test public void childResourceReaderDisablesJLineInPipedDumbTerminal() throws Exception { + var proof = NativePackResourceProof.read(); + var child = proof.getAsJsonObject("childProcess"); + assertEquals("terminal.jline", net.minecrell.terminalconsole.TerminalConsoleAppender.JLINE_OVERRIDE_PROPERTY); + assertEquals("false", child.get("terminalJline").isJsonNull() + ? null : child.get("terminalJline").getAsString()); + assertEquals("dumb", child.get("term").getAsString()); + assertFalse("The child must not attach a console", child.get("consoleAttached").getAsBoolean()); + assertFalse("JLine must not construct a terminal", child.get("terminalCreated").getAsBoolean()); + assertEquals(0, proof.getAsJsonArray("warningsAndErrors").size()); + for (PackType type : PackType.values()) { + assertTrue(type.toString(), proof.getAsJsonObject(type.name()).get("resourcesVerified").getAsInt() > 0); + } + } + + @Test public void terminalWarningsAndResourceErrorsRemainFatal() { + for (String severity : new String[] {"WARN", "ERROR", "FATAL"}) { + String message = severity.equals("WARN") + ? "Advanced terminal features are not available in this environment" : "Resource diagnostic"; + for (String output : new String[] { + "2026-11-12 00:00:00 main " + severity + " " + message, + "[00:00:00] [main/" + severity + "] [NativeProof]: " + message}) { + assertThrows(output, AssertionError.class, + () -> NativePackResourceProof.requireCleanOutput(output, Path.of("negative-control.log"))); + } + } + NativePackResourceProof.requireCleanOutput("[00:00:00] [main/INFO] Native resources loaded", + Path.of("positive-control.log")); + } +} From f64f32d90e1491cac7b404ec8c88845c992fe0df Mon Sep 17 00:00:00 2001 From: JohnBraham Date: Thu, 12 Nov 2026 00:47:09 +0000 Subject: [PATCH 8/8] Use released Power and Base Metals for Steam 1.21.11 --- CHANGELOG.txt | 5 ++--- README.md | 31 +++++++++++++++---------------- docs/PORT12111-STATUS.md | 38 ++++++++++++++++++++++++++++++++------ gradle.properties | 10 +++++----- 4 files changed, 54 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 144804e..489c869 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,6 +1,6 @@ -baseSteam 2.2.16.121111 (Minecraft 1.21.11 / Forge, accepted for release) +baseSteam 2.2.16.121111 (Minecraft 1.21.11 / Forge) -- Target Forge 61.1.0, Java 21 and the pinned local basePower 2.4.15.121111 API. +- Target Forge 61.1.0, Java 21 and released basePower 2.4.15.121111. - Adapt native registration, persistence, recipes, item definitions and drill rendering. - Retain registry identities, legacy state keys, recipe payloads and configuration defaults. - Use native Forge hidden-client pack metadata with data-format 94.1 at the mod root. @@ -9,7 +9,6 @@ baseSteam 2.2.16.121111 (Minecraft 1.21.11 / Forge, accepted for release) - Make the atlas regression test use the configured, checksum-verified Power jar, including builds outside the usual sibling folders. - Keep native resource tests noninteractive in CI without suppressing warnings or errors. - Complete the combined machine, upgrade, graphical and two-world endurance checks against the current jars. -- Accepted for release; dependency publication and hosted release checks are still pending. baseSteam 2.2.15.121011 (Minecraft 1.21.1 / Forge) diff --git a/README.md b/README.md index dcee80c..2c4f5ab 100644 --- a/README.md +++ b/README.md @@ -13,20 +13,19 @@ Target: Forge 61.1.0, official Minecraft 1.21.11 mappings and MCP wrapper use Temurin 21.0.7+6. Mavenizer uses Java 25; the launcher helper also needs Java 8. Minecraft itself runs on Java 21. -The default dependency paths use the coordinated 1.21.11 checkouts. This -candidate needs basePower 2.4.15.121111, Base Metals 3.1.2.121111 and -OreSpawn 4.1.0.121111 as separate native jars, not bundled copies. +Install [basePower 2.4.15.121111](https://github.com/MinecraftModDevelopmentMods/basePower/releases/tag/2.4.15.121111), +[Base Metals 3.1.2.121111](https://www.curseforge.com/minecraft/mc-mods/base-metals/files/9566738) +and [OreSpawn 4.1.0.121111](https://www.curseforge.com/minecraft/mc-mods/mmd-orespawn/files/9268464) +as separate Forge 1.21.11 jars on both client and server, not bundled copies. Base Metals is REQUIRED on both client and server, not an optional integration. -Exact local source, jar and test-bootstrap pins are in `gradle.properties`. -They are checked before compilation and tests. Local approval does not -establish public availability. The Base Metals shared hook and approved Power -integration commit 41959572b11dce236df98f23e97c6a3868cdda03 are pinned locally. -The 1.21.11 port is accepted for release. The current family has passed the -combined machine, upgrade, graphical and two-world 30-minute endurance checks. -Publication is still pending the dependency releases and hosted checks; these -local pins have not been replaced with released inputs. The full record is -in basePower's -`docs/1.21.11/QUALIFICATION.md`. Earlier no-Base-Metals receipts are +The default build paths use the coordinated 1.21.11 checkouts. Exact released +source, jar and test-bootstrap pins are in `gradle.properties` and are checked +before compilation and tests. + +This version has passed automated qualification and manual acceptance. The +[qualification record](https://github.com/MinecraftModDevelopmentMods/basePower/blob/master-1.21.11/docs/1.21.11/QUALIFICATION.md) +records the machine, upgrade, graphical and two-world endurance checks and +their coverage limits. Earlier no-Base-Metals receipts are historical evidence, not a supported installation profile. The mod root declares server data format 94.1. Forge supplies format 75.x metadata for its hidden client resource aggregate. Native resource-reader @@ -39,7 +38,7 @@ can use `-Pforge61MavenizerJar=`. The `*DeobfJar` and `*PlayerJar` properties select separate development and packaged inputs; both must satisfy the committed checksums. -With the pinned local dependencies available: +With the pinned released dependencies available locally: ```powershell $env:JAVA_HOME = "$env:USERPROFILE/.jdks/temurin-21.0.7+6" @@ -75,8 +74,8 @@ when running the packaged family rig; it records its own runtime arguments. Independent source checks: `node tools/verify-native-port.js` and `node tools/generate-recipe-unlocks.js --check`. -Hosted CI and CodeQL require merged sources and checksum-matching public -downloads before compiling. Pending local pins cannot pass that gate. +Hosted CI and CodeQL verify merged dependency sources and checksum-matching +public downloads before compiling. ## Old Worlds diff --git a/docs/PORT12111-STATUS.md b/docs/PORT12111-STATUS.md index 5eaeb1f..7677d11 100644 --- a/docs/PORT12111-STATUS.md +++ b/docs/PORT12111-STATUS.md @@ -1,15 +1,41 @@ # Steam on Minecraft 1.21.11 -## Accepted For Release +## Public Dependency Pins + +The 12 November 2026 build uses released basePower 2.4.15.121111 source +9b0043483d79d3e575895f12aeb0dde7ef6c4890, CurseForge file 9566998, and Base +Metals 3.1.2.121111 source 6f4c44e4d0a68bca3838fb025c09cddfaaa08fe8, +CurseForge file 9566738. Their player hashes remain C98D and 598D; the native +unit-bootstrap and source pins are unchanged. + +The actual releaseJar task was forced with the verified public Power jar. +The resulting Steam player remains +F23DDC4ABB36A1C4C7A4205151FBB6DDFD25397E86909E1C0B146C4089B747E0. +All 324 payload entries, including 69 classes and the manifest, are identical +to the accepted Steam jar. No production or resource change was needed. +Earlier jar-task handoffs checked the existing player bytes but did not prove +a fresh player build; the releaseJar receipt records that correction. + +The default-path full release suite passes all 45 tasks and 252 unit tests +without failures, errors or skips. Fresh Eclipse ECJ output compiles the main, +test and GameTest scopes and executes all 252 unit tests without ignored tests +or assumptions. All four named native source GameTests pass. The independent +source-archive build and packaged Steam restart witness have separate receipts. + +Steam publication is held for its hosted checks and the parent's fresh +final-hash family restart, 1800-second endurance and independent replay gate. +New qualification receipts remain separate from the earlier family below. + +## Accepted Family Proof (11 November) The current `F23DDC4ABB36A1C4C7A4205151FBB6DDFD25397E86909E1C0B146C4089B747E0` player jar was accepted for release on 11 November 2026 with Power C98D, Electric 5466, Base Metals 598D and OreSpawn DE39. Publication was authorized -in dependency order, but has not happened yet. The local dependency pins are -unchanged pending the released dependency source, jar hash and public file ID. +in dependency order. This section records the pre-public-pin family, including +the earlier Electric 5466 jar; it does not qualify the later Electric 7128 jar. basePower's `docs/1.21.11/QUALIFICATION.md` records the full hashes and limits. -The current frozen family passes 132 finite jobs, upgrades, chunk/network and +That frozen family passes 132 finite jobs, upgrades, chunk/network and drill lifecycle tests, recipes, world generation, oils, graphical checks and two 30-minute endurance worlds with full restarts. Independent replay passes all 66 checkpoints and exact final stopped machine data. These are new @@ -30,7 +56,7 @@ change was needed during this rerun. Connected-player interaction and live-world visual limits remain recorded; acceptance does not relabel unrun checks as passes. -## Publication Preparation +## Historical Publication Preparation The following is the preparation status on 11 November 2026. @@ -72,7 +98,7 @@ These local edits are not a frozen release or whole-world qualification. Power's approved metadata source 41959572b11dce236df98f23e97c6a3868cdda03 and SHA-256 C98D2CF163BE2480D9AFB0023BBD2D92D401AE0D50BEF73191235BF3F83774A7 -are now pinned for the consistent default build. The RED attempt used the +were pinned for the default build at that stage. The RED attempt used the preserved, hash-checked D input without changing its committed pins at that stage. No OreSpawn change, publication, version bump or packaged-world/endurance campaign is part of this correction. The smoke helper rejects its former diff --git a/gradle.properties b/gradle.properties index 6a802ac..56111c0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -42,9 +42,9 @@ cf_versions=1.21.11 poweradvantage_version=2.4.15.121111 poweradvantage_source_repository=MinecraftModDevelopmentMods/basePower -# Exact approved LOCAL integration candidate; no public 1.21.11 release is implied. -poweradvantage_source_ref=41959572b11dce236df98f23e97c6a3868cdda03 -poweradvantage_public_file_id= +# Exact merged source and independently verified public Forge 1.21.11 release. +poweradvantage_source_ref=9b0043483d79d3e575895f12aeb0dde7ef6c4890 +poweradvantage_public_file_id=9566998 poweradvantage_deobf_sha256=C98D2CF163BE2480D9AFB0023BBD2D92D401AE0D50BEF73191235BF3F83774A7 # Forge 61 uses the same native jar for development and packaged runtime. poweradvantage_player_sha256=C98D2CF163BE2480D9AFB0023BBD2D92D401AE0D50BEF73191235BF3F83774A7 @@ -53,8 +53,8 @@ poweradvantage_test_bootstrap_source_sha256=CDE40D09A40DB95F293BC56723E70B321C20 poweradvantage_test_registry_source_sha256=01DDE50C9874C2DC501BE2F699CD8D16D6974FACFAAC1B803434CA8C64227ABF basemetals_version=3.1.2.121111 -basemetals_source_commit=616d527e50e4bf0137eeb9c44fa586e34a0452a0 -basemetals_public_file_id= +basemetals_source_commit=6f4c44e4d0a68bca3838fb025c09cddfaaa08fe8 +basemetals_public_file_id=9566738 basemetals_deobf_sha256=598D3FB0FF956EBCC67FE477A01CE393C75D0013AF0CE00A79E2072B63E9F2BA basemetals_player_sha256=598D3FB0FF956EBCC67FE477A01CE393C75D0013AF0CE00A79E2072B63E9F2BA