From 5568b3cacd2f837e744b622ce1ff7f2c54c42a00 Mon Sep 17 00:00:00 2001 From: JohnBraham Date: Sun, 8 Nov 2026 16:36:10 +0000 Subject: [PATCH] Update Modern Metals for Minecraft 1.12.2 Use the shared MMDLib crusher registry and the OreSpawn 4 provider. Follow Base Metals Fantasy Level when it is installed and keep the standalone setting. --- .gitattributes | 11 + .github/workflows/ci.yml | 230 +++++++ .github/workflows/codeql-analysis.yml | 80 +++ .github/workflows/deploy-release.yml | 12 +- .github/workflows/release-on-tag.yml | 89 +++ .github/workflows/validate-gradle-build.yml | 23 + .gitignore | 16 +- .travis.yml | 29 - CHANGELOG.txt | 10 + README.md | 114 ++-- build.gradle | 586 +++++------------- gradle.properties | 84 +-- gradle/dependencies.gradle | 65 ++ gradle/dependencies.json | 58 ++ gradle/ide/eclipse.gradle | 206 ++++++ gradle/ide/log4j2.xml | 21 + gradle/ore-models.gradle | 71 +++ gradle/prepare-packaged-forge-runtime.sh | 37 ++ gradle/release/artifacts.gradle | 185 ++++++ gradle/release/publishing.gradle | 96 +++ gradle/stage-legacy-dependencies.py | 53 ++ gradle/stage-legacy-dependencies.sh | 9 + gradle/verification/runtime.gradle | 35 ++ gradle/wrapper/gradle-wrapper.jar | Bin 54413 -> 48462 bytes gradle/wrapper/gradle-wrapper.properties | 7 +- gradlew | 302 +++++---- gradlew.bat | 100 ++- settings.gradle | 7 +- .../mcmoddev/modernmetals/init/Recipes.java | 70 --- .../modernmetals/init/VillagerTrades.java | 21 - .../modernmetals/proxy/CommonProxy.java | 69 --- .../orespawn/ModernMetalsOreSpawn.java | 16 - .../moddev/mc}/modernmetals/ModernMetals.java | 26 +- .../client/ModernMetalsConfigScreen.java | 36 ++ .../client/ModernMetalsGuiFactory.java | 32 + .../compat/LegacyNamespaceTransformer.java | 89 +++ .../compat/ModernMetalsLoadingPlugin.java | 19 + .../content/ContentModeNetwork.java | 64 ++ .../modernmetals/content/ContentPolicy.java | 79 +++ .../mc}/modernmetals/data/MaterialNames.java | 2 +- .../mc}/modernmetals/data/TraitNames.java | 2 +- .../moddev/mc}/modernmetals/init/Blocks.java | 6 +- .../moddev/mc}/modernmetals/init/Fluids.java | 19 +- .../mc}/modernmetals/init/ItemGroups.java | 4 +- .../moddev/mc}/modernmetals/init/Items.java | 6 +- .../mc}/modernmetals/init/Materials.java | 4 +- .../mc/modernmetals/init/PolicyRecipe.java | 66 ++ .../moddev/mc/modernmetals/init/Recipes.java | 116 ++++ .../mc/modernmetals/init/VillagerTrades.java | 46 ++ .../plugins/MMeConstructsArmory.java | 6 +- .../integration/plugins/MMeDenseOres.java | 6 +- .../integration/plugins/MMeEnderIO.java | 6 +- .../integration/plugins/MMeIC2.java | 6 +- .../integration/plugins/MMeMekanism.java | 6 +- .../integration/plugins/MMeThaumcraft.java | 4 +- .../plugins/MMeThermalExpansion.java | 20 +- .../plugins/MMeTinkersConstruct.java | 75 ++- .../integration/plugins/MMeVeinMiner.java | 6 +- .../mc}/modernmetals/proxy/ClientProxy.java | 18 +- .../mc/modernmetals/proxy/CommonProxy.java | 50 ++ .../mc}/modernmetals/proxy/ServerProxy.java | 2 +- .../mc}/modernmetals/util/EventHandler.java | 138 ++--- .../mc}/modernmetals/util/MMeConfig.java | 54 +- .../ModernMetalsWorldgenProvider.java | 85 +++ .../META-INF/modernmetals-legacy-api.list | 24 + .../assets/modernmetals/lang/de_DE.lang | 38 +- .../assets/modernmetals/lang/en_US.lang | 36 ++ .../assets/modernmetals/lang/en_au.lang | 36 ++ .../assets/modernmetals/lang/en_ca.lang | 36 ++ .../assets/modernmetals/lang/en_gb.lang | 36 ++ .../assets/modernmetals/lang/en_nz.lang | 36 ++ .../assets/modernmetals/lang/en_pt.lang | 36 ++ .../assets/modernmetals/lang/ru_RU.lang | 36 ++ .../assets/modernmetals/lang/zh_CN.lang | 36 +- .../models/block/aluminum_ore.json | 6 - .../models/block/aluminum_plate.json | 2 +- .../models/block/aluminumbrass_ore.json | 6 - .../models/block/aluminumbrass_plate.json | 2 +- .../models/block/beryllium_ore.json | 6 - .../models/block/beryllium_plate.json | 2 +- .../modernmetals/models/block/boron_ore.json | 6 - .../models/block/boron_plate.json | 2 +- .../models/block/cadmium_ore.json | 6 - .../models/block/cadmium_plate.json | 2 +- .../models/block/chromium_ore.json | 6 - .../models/block/chromium_plate.json | 2 +- .../models/block/galvanizedsteel_ore.json | 6 - .../models/block/galvanizedsteel_plate.json | 2 +- .../models/block/iridium_ore.json | 6 - .../models/block/iridium_plate.json | 2 +- .../models/block/magnesium_ore.json | 6 - .../models/block/magnesium_plate.json | 2 +- .../models/block/manganese_ore.json | 6 - .../models/block/manganese_plate.json | 2 +- .../models/block/nichrome_ore.json | 6 - .../models/block/nichrome_plate.json | 2 +- .../modernmetals/models/block/osmium_ore.json | 6 - .../models/block/osmium_plate.json | 2 +- .../modernmetals/models/block/plate.json | 50 ++ .../models/block/plutonium_ore.json | 6 - .../models/block/plutonium_plate.json | 2 +- .../modernmetals/models/block/rutile_ore.json | 6 - .../models/block/rutile_plate.json | 2 +- .../models/block/stainlesssteel_ore.json | 6 - .../models/block/stainlesssteel_plate.json | 2 +- .../models/block/tantalum_ore.json | 6 - .../models/block/tantalum_plate.json | 2 +- .../models/block/thorium_plate.json | 2 +- .../models/block/titanium_ore.json | 6 - .../models/block/titanium_plate.json | 2 +- .../models/block/tungsten_ore.json | 6 - .../models/block/tungsten_plate.json | 2 +- .../models/block/uranium_ore.json | 6 - .../models/block/uranium_plate.json | 2 +- .../models/block/zirconium_ore.json | 6 - .../models/block/zirconium_plate.json | 2 +- .../models/item/aluminum_door.json | 2 +- .../models/item/aluminum_door_item.json | 2 +- .../models/item/aluminumbrass_door.json | 2 +- .../models/item/aluminumbrass_door_item.json | 2 +- .../models/item/beryllium_door.json | 2 +- .../models/item/beryllium_door_item.json | 2 +- .../modernmetals/models/item/boron_door.json | 2 +- .../models/item/boron_door_item.json | 2 +- .../models/item/cadmium_door.json | 2 +- .../models/item/cadmium_door_item.json | 2 +- .../models/item/chromium_door.json | 2 +- .../models/item/chromium_door_item.json | 2 +- .../modernmetals/models/item/door_item.json | 57 ++ .../models/item/galvanizedsteel_door.json | 2 +- .../item/galvanizedsteel_door_item.json | 2 +- .../models/item/iridium_door.json | 2 +- .../models/item/iridium_door_item.json | 2 +- .../models/item/magnesium_door.json | 2 +- .../models/item/magnesium_door_item.json | 2 +- .../models/item/manganese_door.json | 2 +- .../models/item/manganese_door_item.json | 2 +- .../models/item/nichrome_door.json | 2 +- .../models/item/nichrome_door_item.json | 2 +- .../modernmetals/models/item/osmium_door.json | 2 +- .../models/item/osmium_door_item.json | 2 +- .../models/item/plutonium_door.json | 2 +- .../models/item/plutonium_door_item.json | 2 +- .../modernmetals/models/item/rutile_door.json | 2 +- .../models/item/rutile_door_item.json | 2 +- .../models/item/silicon_block.json | 3 - .../models/item/silicon_door.json | 2 +- .../models/item/silicon_door_item.json | 2 +- .../modernmetals/models/item/silicon_ore.json | 3 - .../models/item/silicon_plate.json | 3 - .../models/item/silicon_trapdoor.json | 3 - .../models/item/stainlesssteel_door.json | 2 +- .../models/item/stainlesssteel_door_item.json | 2 +- .../models/item/tantalum_door.json | 2 +- .../models/item/tantalum_door_item.json | 2 +- .../models/item/thorium_door.json | 2 +- .../models/item/thorium_door_item.json | 2 +- .../models/item/titanium_door.json | 2 +- .../models/item/titanium_door_item.json | 2 +- .../models/item/tungsten_door.json | 2 +- .../models/item/tungsten_door_item.json | 2 +- .../models/item/uranium_door.json | 2 +- .../models/item/uranium_door_item.json | 2 +- .../models/item/zirconium_door.json | 2 +- .../models/item/zirconium_door_item.json | 2 +- .../recipes/nichrome_blend_base.json | 4 +- .../recipes/stainlesssteel_blend_base.json | 4 +- .../textures/blocks/aluminum_ore.png | Bin 470 -> 0 bytes .../textures/blocks/aluminumbrass_ore.png | Bin 470 -> 0 bytes .../textures/blocks/beryllium_ore.png | Bin 470 -> 0 bytes .../textures/blocks/boron_ore.png | Bin 418 -> 0 bytes .../textures/blocks/cadmium_ore.png | Bin 470 -> 0 bytes .../textures/blocks/chromium_ore.png | Bin 470 -> 0 bytes .../textures/blocks/galvanizedsteel_ore.png | Bin 470 -> 0 bytes .../textures/blocks/iridium_ore.png | Bin 470 -> 0 bytes .../textures/blocks/magnesium_ore.png | Bin 470 -> 0 bytes .../textures/blocks/manganese_ore.png | Bin 470 -> 0 bytes .../textures/blocks/nichrome_ore.png | Bin 470 -> 0 bytes .../blocks/ore_overlays/aluminum_ore.png | Bin 0 -> 391 bytes .../blocks/ore_overlays/aluminumbrass_ore.png | Bin 0 -> 418 bytes .../blocks/ore_overlays/beryllium_ore.png | Bin 0 -> 387 bytes .../blocks/ore_overlays/boron_ore.png | Bin 0 -> 331 bytes .../blocks/ore_overlays/cadmium_ore.png | Bin 0 -> 405 bytes .../blocks/ore_overlays/chromium_ore.png | Bin 0 -> 384 bytes .../ore_overlays/galvanizedsteel_ore.png | Bin 0 -> 398 bytes .../blocks/ore_overlays/iridium_ore.png | Bin 0 -> 408 bytes .../blocks/ore_overlays/magnesium_ore.png | Bin 0 -> 388 bytes .../blocks/ore_overlays/manganese_ore.png | Bin 0 -> 414 bytes .../blocks/ore_overlays/nichrome_ore.png | Bin 0 -> 421 bytes .../blocks/ore_overlays/osmium_ore.png | Bin 0 -> 407 bytes .../blocks/ore_overlays/plutonium_ore.png | Bin 0 -> 419 bytes .../blocks/ore_overlays/rutile_ore.png | Bin 0 -> 411 bytes .../ore_overlays/stainlesssteel_ore.png | Bin 0 -> 394 bytes .../blocks/ore_overlays/tantalum_ore.png | Bin 0 -> 390 bytes .../blocks/ore_overlays/titanium_ore.png | Bin 0 -> 398 bytes .../blocks/ore_overlays/tungsten_ore.png | Bin 0 -> 381 bytes .../blocks/ore_overlays/uranium_ore.png | Bin 0 -> 412 bytes .../blocks/ore_overlays/zirconium_ore.png | Bin 0 -> 400 bytes .../textures/blocks/osmium_ore.png | Bin 470 -> 0 bytes .../textures/blocks/plutonium_ore.png | Bin 470 -> 0 bytes .../textures/blocks/rutile_ore.png | Bin 470 -> 0 bytes .../textures/blocks/stainlesssteel_ore.png | Bin 470 -> 0 bytes .../textures/blocks/tantalum_ore.png | Bin 470 -> 0 bytes .../textures/blocks/titanium_ore.png | Bin 470 -> 0 bytes .../textures/blocks/tungsten_ore.png | Bin 414 -> 0 bytes .../textures/blocks/uranium_ore.png | Bin 470 -> 0 bytes .../textures/blocks/zirconium_ore.png | Bin 470 -> 0 bytes src/main/resources/pack.mcmeta | 4 +- tools/validation/legacy112/audit_resources.py | 30 + .../fixtures/orespawn3-modernmetals.json | 2 +- .../legacy112/run_packaged_suite.py | 161 +++++ .../review/modern/ClientChecks.java | 97 +++ .../review/modern/ModernMetalsProbe.java | 164 +++++ .../review/modern/TinkersChecks.java | 42 ++ tools/validation/requirements.txt | 2 + 215 files changed, 3720 insertions(+), 1319 deletions(-) create mode 100644 .gitattributes create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/codeql-analysis.yml create mode 100644 .github/workflows/release-on-tag.yml create mode 100644 .github/workflows/validate-gradle-build.yml delete mode 100644 .travis.yml create mode 100644 gradle/dependencies.gradle create mode 100644 gradle/dependencies.json create mode 100644 gradle/ide/eclipse.gradle create mode 100644 gradle/ide/log4j2.xml create mode 100644 gradle/ore-models.gradle create mode 100644 gradle/prepare-packaged-forge-runtime.sh create mode 100644 gradle/release/artifacts.gradle create mode 100644 gradle/release/publishing.gradle create mode 100644 gradle/stage-legacy-dependencies.py create mode 100644 gradle/stage-legacy-dependencies.sh create mode 100644 gradle/verification/runtime.gradle delete mode 100644 src/main/java/com/mcmoddev/modernmetals/init/Recipes.java delete mode 100644 src/main/java/com/mcmoddev/modernmetals/init/VillagerTrades.java delete mode 100644 src/main/java/com/mcmoddev/modernmetals/proxy/CommonProxy.java delete mode 100644 src/main/java/com/mcmoddev/orespawn/ModernMetalsOreSpawn.java rename src/main/java/{com/mcmoddev => zone/moddev/mc}/modernmetals/ModernMetals.java (74%) create mode 100644 src/main/java/zone/moddev/mc/modernmetals/client/ModernMetalsConfigScreen.java create mode 100644 src/main/java/zone/moddev/mc/modernmetals/client/ModernMetalsGuiFactory.java create mode 100644 src/main/java/zone/moddev/mc/modernmetals/compat/LegacyNamespaceTransformer.java create mode 100644 src/main/java/zone/moddev/mc/modernmetals/compat/ModernMetalsLoadingPlugin.java create mode 100644 src/main/java/zone/moddev/mc/modernmetals/content/ContentModeNetwork.java create mode 100644 src/main/java/zone/moddev/mc/modernmetals/content/ContentPolicy.java rename src/main/java/{com/mcmoddev => zone/moddev/mc}/modernmetals/data/MaterialNames.java (97%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/modernmetals/data/TraitNames.java (94%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/modernmetals/init/Blocks.java (94%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/modernmetals/init/Fluids.java (66%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/modernmetals/init/ItemGroups.java (93%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/modernmetals/init/Items.java (97%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/modernmetals/init/Materials.java (98%) create mode 100644 src/main/java/zone/moddev/mc/modernmetals/init/PolicyRecipe.java create mode 100644 src/main/java/zone/moddev/mc/modernmetals/init/Recipes.java create mode 100644 src/main/java/zone/moddev/mc/modernmetals/init/VillagerTrades.java rename src/main/java/{com/mcmoddev => zone/moddev/mc}/modernmetals/integration/plugins/MMeConstructsArmory.java (95%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/modernmetals/integration/plugins/MMeDenseOres.java (90%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/modernmetals/integration/plugins/MMeEnderIO.java (88%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/modernmetals/integration/plugins/MMeIC2.java (95%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/modernmetals/integration/plugins/MMeMekanism.java (93%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/modernmetals/integration/plugins/MMeThaumcraft.java (80%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/modernmetals/integration/plugins/MMeThermalExpansion.java (79%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/modernmetals/integration/plugins/MMeTinkersConstruct.java (75%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/modernmetals/integration/plugins/MMeVeinMiner.java (89%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/modernmetals/proxy/ClientProxy.java (66%) create mode 100644 src/main/java/zone/moddev/mc/modernmetals/proxy/CommonProxy.java rename src/main/java/{com/mcmoddev => zone/moddev/mc}/modernmetals/proxy/ServerProxy.java (77%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/modernmetals/util/EventHandler.java (82%) rename src/main/java/{com/mcmoddev => zone/moddev/mc}/modernmetals/util/MMeConfig.java (54%) create mode 100644 src/main/java/zone/moddev/mc/modernmetals/worldgen/ModernMetalsWorldgenProvider.java create mode 100644 src/main/resources/META-INF/modernmetals-legacy-api.list delete mode 100644 src/main/resources/assets/modernmetals/models/block/aluminum_ore.json delete mode 100644 src/main/resources/assets/modernmetals/models/block/aluminumbrass_ore.json delete mode 100644 src/main/resources/assets/modernmetals/models/block/beryllium_ore.json delete mode 100644 src/main/resources/assets/modernmetals/models/block/boron_ore.json delete mode 100644 src/main/resources/assets/modernmetals/models/block/cadmium_ore.json delete mode 100644 src/main/resources/assets/modernmetals/models/block/chromium_ore.json delete mode 100644 src/main/resources/assets/modernmetals/models/block/galvanizedsteel_ore.json delete mode 100644 src/main/resources/assets/modernmetals/models/block/iridium_ore.json delete mode 100644 src/main/resources/assets/modernmetals/models/block/magnesium_ore.json delete mode 100644 src/main/resources/assets/modernmetals/models/block/manganese_ore.json delete mode 100644 src/main/resources/assets/modernmetals/models/block/nichrome_ore.json delete mode 100644 src/main/resources/assets/modernmetals/models/block/osmium_ore.json create mode 100644 src/main/resources/assets/modernmetals/models/block/plate.json delete mode 100644 src/main/resources/assets/modernmetals/models/block/plutonium_ore.json delete mode 100644 src/main/resources/assets/modernmetals/models/block/rutile_ore.json delete mode 100644 src/main/resources/assets/modernmetals/models/block/stainlesssteel_ore.json delete mode 100644 src/main/resources/assets/modernmetals/models/block/tantalum_ore.json delete mode 100644 src/main/resources/assets/modernmetals/models/block/titanium_ore.json delete mode 100644 src/main/resources/assets/modernmetals/models/block/tungsten_ore.json delete mode 100644 src/main/resources/assets/modernmetals/models/block/uranium_ore.json delete mode 100644 src/main/resources/assets/modernmetals/models/block/zirconium_ore.json create mode 100644 src/main/resources/assets/modernmetals/models/item/door_item.json delete mode 100644 src/main/resources/assets/modernmetals/models/item/silicon_block.json delete mode 100644 src/main/resources/assets/modernmetals/models/item/silicon_ore.json delete mode 100644 src/main/resources/assets/modernmetals/models/item/silicon_plate.json delete mode 100644 src/main/resources/assets/modernmetals/models/item/silicon_trapdoor.json delete mode 100644 src/main/resources/assets/modernmetals/textures/blocks/aluminum_ore.png delete mode 100644 src/main/resources/assets/modernmetals/textures/blocks/aluminumbrass_ore.png delete mode 100644 src/main/resources/assets/modernmetals/textures/blocks/beryllium_ore.png delete mode 100644 src/main/resources/assets/modernmetals/textures/blocks/boron_ore.png delete mode 100644 src/main/resources/assets/modernmetals/textures/blocks/cadmium_ore.png delete mode 100644 src/main/resources/assets/modernmetals/textures/blocks/chromium_ore.png delete mode 100644 src/main/resources/assets/modernmetals/textures/blocks/galvanizedsteel_ore.png delete mode 100644 src/main/resources/assets/modernmetals/textures/blocks/iridium_ore.png delete mode 100644 src/main/resources/assets/modernmetals/textures/blocks/magnesium_ore.png delete mode 100644 src/main/resources/assets/modernmetals/textures/blocks/manganese_ore.png delete mode 100644 src/main/resources/assets/modernmetals/textures/blocks/nichrome_ore.png create mode 100644 src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/aluminum_ore.png create mode 100644 src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/aluminumbrass_ore.png create mode 100644 src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/beryllium_ore.png create mode 100644 src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/boron_ore.png create mode 100644 src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/cadmium_ore.png create mode 100644 src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/chromium_ore.png create mode 100644 src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/galvanizedsteel_ore.png create mode 100644 src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/iridium_ore.png create mode 100644 src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/magnesium_ore.png create mode 100644 src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/manganese_ore.png create mode 100644 src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/nichrome_ore.png create mode 100644 src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/osmium_ore.png create mode 100644 src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/plutonium_ore.png create mode 100644 src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/rutile_ore.png create mode 100644 src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/stainlesssteel_ore.png create mode 100644 src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/tantalum_ore.png create mode 100644 src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/titanium_ore.png create mode 100644 src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/tungsten_ore.png create mode 100644 src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/uranium_ore.png create mode 100644 src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/zirconium_ore.png delete mode 100644 src/main/resources/assets/modernmetals/textures/blocks/osmium_ore.png delete mode 100644 src/main/resources/assets/modernmetals/textures/blocks/plutonium_ore.png delete mode 100644 src/main/resources/assets/modernmetals/textures/blocks/rutile_ore.png delete mode 100644 src/main/resources/assets/modernmetals/textures/blocks/stainlesssteel_ore.png delete mode 100644 src/main/resources/assets/modernmetals/textures/blocks/tantalum_ore.png delete mode 100644 src/main/resources/assets/modernmetals/textures/blocks/titanium_ore.png delete mode 100644 src/main/resources/assets/modernmetals/textures/blocks/tungsten_ore.png delete mode 100644 src/main/resources/assets/modernmetals/textures/blocks/uranium_ore.png delete mode 100644 src/main/resources/assets/modernmetals/textures/blocks/zirconium_ore.png create mode 100644 tools/validation/legacy112/audit_resources.py rename src/main/resources/assets/modernmetals/orespawn/modernmetals.json => tools/validation/legacy112/fixtures/orespawn3-modernmetals.json (99%) create mode 100644 tools/validation/legacy112/run_packaged_suite.py create mode 100644 tools/validation/legacy112/runtime-probe/review/modern/ClientChecks.java create mode 100644 tools/validation/legacy112/runtime-probe/review/modern/ModernMetalsProbe.java create mode 100644 tools/validation/legacy112/runtime-probe/review/modern/TinkersChecks.java create mode 100644 tools/validation/requirements.txt diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..2dc7c427 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +* text=auto +*.java text eol=lf +*.gradle text eol=lf +*.json text eol=lf +*.yml text eol=lf +*.py text eol=lf +*.sh text eol=lf +gradlew text eol=lf +gradlew.bat text eol=crlf +*.png binary +*.jar binary diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..9fb4fda5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,230 @@ +name: Modern Metals 1.12 CI + +on: + push: + branches: + - master-1.12 + - 'feature/**' + pull_request: + branches: + - master-1.12 + +permissions: + contents: read + +concurrency: + group: modernmetals-1.12-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + cold-forge-bootstrap: + name: Cold Forge bootstrap + runs-on: ubuntu-latest + timeout-minutes: 75 + + steps: + - name: Check out source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + + - name: Install pinned Java 25 ForgeGradle Mavenizer runtime + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: '25.0.3+9.0.LTS' + + - name: Install pinned Java 8 compilation toolchain + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: '8.0.502+7' + + - name: Install pinned Java 17 Gradle runtime + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: '17.0.1+12' + + - name: Stage checksum-verified legacy dependencies + id: dependencies + shell: bash + run: | + set -euo pipefail + mirror="$RUNNER_TEMP/legacy-dependency-mirror" + bash gradle/stage-legacy-dependencies.sh "$mirror" + echo "repository=$mirror" >> "$GITHUB_OUTPUT" + + - name: Bootstrap Forge from an empty cache + shell: bash + env: + GRADLE_USER_HOME: ${{ runner.temp }}/modernmetals-cold-gradle + run: | + set -euo pipefail + test ! -e .gradle + test ! -e "$GRADLE_USER_HOME" + mkdir -p "$GRADLE_USER_HOME" + chmod +x ./gradlew + gradle_args=( + classes verifyReleaseDependencies + -PlegacyDependencyVerificationRepository="${{ steps.dependencies.outputs.repository }}" + --no-daemon --no-build-cache --stacktrace --max-workers=2 + -Porg.gradle.java.installations.paths="$JAVA_HOME,$JAVA_HOME_8_X64,$JAVA_HOME_25_X64" + -Porg.gradle.java.installations.auto-detect=false + -Porg.gradle.java.installations.auto-download=false + ) + ./gradlew "${gradle_args[@]}" + + - name: Verify same-cache bootstrap offline + shell: bash + env: + GRADLE_USER_HOME: ${{ runner.temp }}/modernmetals-cold-gradle + run: | + set -euo pipefail + gradle_args=( + classes verifyReleaseDependencies + -PlegacyDependencyVerificationRepository="${{ steps.dependencies.outputs.repository }}" + --rerun-tasks --offline --no-daemon --no-build-cache --stacktrace --max-workers=2 + -Porg.gradle.java.installations.paths="$JAVA_HOME,$JAVA_HOME_8_X64,$JAVA_HOME_25_X64" + -Porg.gradle.java.installations.auto-detect=false + -Porg.gradle.java.installations.auto-download=false + ) + ./gradlew "${gradle_args[@]}" + + build: + name: Build, test, and audit + runs-on: ubuntu-latest + timeout-minutes: 90 + + steps: + - name: Check out source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + + - name: Install pinned Java 25 ForgeGradle Mavenizer runtime + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: '25.0.3+9.0.LTS' + + - name: Install pinned Java 8 compilation and runtime toolchain + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: '8.0.502+7' + + - name: Install pinned Java 17 Gradle runtime + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: '17.0.1+12' + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6 + + - name: Stage checksum-verified dependencies + id: dependencies + shell: bash + run: | + set -euo pipefail + mirror="$RUNNER_TEMP/legacy-dependency-mirror" + bash gradle/stage-legacy-dependencies.sh "$mirror" + echo "repository=$mirror" >> "$GITHUB_OUTPUT" + + - name: Prepare clean packaged Forge runtime + id: forge + shell: bash + run: | + set -euo pipefail + runtime="$RUNNER_TEMP/forge-1.12.2-2859-runtime" + bash gradle/prepare-packaged-forge-runtime.sh "$runtime" "$JAVA_HOME_8_X64/bin/java" + echo "runtime=$runtime" >> "$GITHUB_OUTPUT" + + - name: Set up Python for the provider audit + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 + with: + python-version: '3.12' + + - name: Install the pinned provider schema validator + run: python -m pip install -r tools/validation/requirements.txt + + - name: Build, test, audit, qualify runtimes, and generate Eclipse launches + shell: bash + run: | + set -euo pipefail + chmod +x ./gradlew + gradle_args=( + clean check build javadoc verifyReleaseDependencies verifyReleaseArtifacts + writeReleaseChecksums prepareEclipse verifyProjectResources packagedForgeRuntimeTest + -PpythonExecutable=python3 + -PpackagedForgeRuntimeOutput="$RUNNER_TEMP/modernmetals-packaged-checks" + -PreleaseBuild=true + -PlegacyDependencyVerificationRepository="${{ steps.dependencies.outputs.repository }}" + -PpackagedForgeRuntimeRoot="${{ steps.forge.outputs.runtime }}" + --no-daemon --stacktrace --max-workers=2 + -Porg.gradle.java.installations.paths="$JAVA_HOME,$JAVA_HOME_8_X64,$JAVA_HOME_25_X64" + -Porg.gradle.java.installations.auto-detect=false + -Porg.gradle.java.installations.auto-download=false + ) + ./gradlew "${gradle_args[@]}" + cp build/release/SHA256SUMS "$RUNNER_TEMP/first-SHA256SUMS" + + - name: Verify the prepared release bundle + shell: bash + run: | + set -euo pipefail + bundle="$RUNNER_TEMP/release-input" + mkdir -p "$bundle" + cp build/libs/ModernMetals-2.5.1.112021{,-sources,-javadoc}.jar "$bundle/" + cp build/release/SHA256SUMS CHANGELOG.txt "$bundle/" + ./gradlew verifyPreparedReleaseArtifacts verifyMavenCoordinates \ + -PpreparedReleaseDir="$bundle" \ + -PlegacyDependencyVerificationRepository="${{ steps.dependencies.outputs.repository }}" \ + -Porg.gradle.java.installations.paths="$JAVA_HOME,$JAVA_HOME_8_X64,$JAVA_HOME_25_X64" \ + -Porg.gradle.java.installations.auto-detect=false \ + -Porg.gradle.java.installations.auto-download=false \ + --offline --no-daemon --stacktrace --max-workers=2 + + - name: Verify reproducible release archives + shell: bash + run: | + set -euo pipefail + gradle_args=( + clean build javadoc verifyReleaseArtifacts writeReleaseChecksums + -PreleaseBuild=true + -PlegacyDependencyVerificationRepository="${{ steps.dependencies.outputs.repository }}" + --offline --no-daemon --no-build-cache --stacktrace --max-workers=2 + -Porg.gradle.java.installations.paths="$JAVA_HOME,$JAVA_HOME_8_X64,$JAVA_HOME_25_X64" + -Porg.gradle.java.installations.auto-detect=false + -Porg.gradle.java.installations.auto-download=false + ) + ./gradlew "${gradle_args[@]}" + cmp "$RUNNER_TEMP/first-SHA256SUMS" build/release/SHA256SUMS + + - name: Upload audited release candidate + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: ModernMetals-1.12.2-${{ github.sha }} + if-no-files-found: error + retention-days: 30 + path: | + build/libs/ModernMetals-2.5.1.112021.jar + build/libs/ModernMetals-2.5.1.112021-sources.jar + build/libs/ModernMetals-2.5.1.112021-javadoc.jar + build/release/SHA256SUMS + CHANGELOG.txt + + - name: Upload bounded diagnostics on failure + if: failure() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: ModernMetals-1.12.2-diagnostics-${{ github.sha }} + if-no-files-found: ignore + retention-days: 14 + path: | + build/test-results/** + build/reports/** + ${{ runner.temp }}/modernmetals-packaged-checks/**/*-console.log + ${{ runner.temp }}/modernmetals-packaged-checks/**/logs/*.log + ${{ runner.temp }}/modernmetals-packaged-checks/**/crash-reports/*.txt + ${{ runner.temp }}/modernmetals-packaged-checks/**/*-modern.json + ${{ runner.temp }}/modernmetals-packaged-checks/**/*-review.json + ${{ runner.temp }}/modernmetals-packaged-checks/**/*-launch.json + build/problems/** diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000..1b38e932 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,80 @@ +name: CodeQL + +on: + push: + branches: + - master-1.12 + - 'feature/**' + pull_request: + branches: + - master-1.12 + schedule: + - cron: '47 7 * * 4' + +permissions: + actions: read + contents: read + security-events: write + +jobs: + analyze: + name: Analyze Java + runs-on: ubuntu-latest + timeout-minutes: 60 + + steps: + - name: Check out source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + + - name: Install pinned Java 25 ForgeGradle Mavenizer runtime + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: '25.0.3+9.0.LTS' + + - name: Install pinned Java 8 compilation toolchain + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: '8.0.502+7' + + - name: Install pinned Java 17 Gradle runtime + uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + with: + distribution: temurin + java-version: '17.0.1+12' + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6 + + - name: Initialize CodeQL + uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4 + with: + languages: java-kotlin + + - name: Stage checksum-verified dependencies + id: dependencies + shell: bash + run: | + set -euo pipefail + mirror="$RUNNER_TEMP/legacy-dependency-mirror" + bash gradle/stage-legacy-dependencies.sh "$mirror" + echo "repository=$mirror" >> "$GITHUB_OUTPUT" + + - name: Compile production code + shell: bash + run: | + set -euo pipefail + chmod +x ./gradlew + gradle_args=( + clean classes + -PlegacyDependencyVerificationRepository="${{ steps.dependencies.outputs.repository }}" + --no-daemon --no-build-cache --stacktrace --max-workers=2 + -Porg.gradle.java.installations.paths="$JAVA_HOME,$JAVA_HOME_8_X64,$JAVA_HOME_25_X64" + -Porg.gradle.java.installations.auto-detect=false + -Porg.gradle.java.installations.auto-download=false + ) + ./gradlew "${gradle_args[@]}" + + - name: Analyze + uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4 diff --git a/.github/workflows/deploy-release.yml b/.github/workflows/deploy-release.yml index ff396ac4..8b698848 100644 --- a/.github/workflows/deploy-release.yml +++ b/.github/workflows/deploy-release.yml @@ -89,8 +89,8 @@ jobs: exit 1 fi - if [[ "$target_suffix" != 110021 ]]; then - echo 'Only the qualified Minecraft 1.10.2 Forge target is supported by this dispatcher' >&2; exit 1 + if [[ "$target_suffix" != 110021 && "$target_suffix" != 111021 && "$target_suffix" != 112021 ]]; then + echo 'Only the Minecraft 1.10.2, 1.11.2 and 1.12.2 Forge targets are supported by this dispatcher' >&2; exit 1 fi loader_code="${target_suffix: -1}" @@ -295,8 +295,12 @@ jobs: fi curseforge_dependencies+=('243157(optional)') - if [[ " ${curseforge_dependencies[*]} " != *' 240967(required) '* ]]; then - echo 'Base Metals must be a required relation for this 1.10 release' >&2; exit 1 + if [[ "$target_suffix" == 112021 ]]; then + if [[ " ${curseforge_dependencies[*]} " != *' 261744(required) '* ]]; then + echo 'MMDLib must be a required relation for the Minecraft 1.12.2 release' >&2; exit 1 + fi + elif [[ " ${curseforge_dependencies[*]} " != *' 240967(required) '* ]]; then + echo 'Base Metals must be a required relation for this target' >&2; exit 1 fi release_sha="$(git rev-parse HEAD)" diff --git a/.github/workflows/release-on-tag.yml b/.github/workflows/release-on-tag.yml new file mode 100644 index 00000000..718b4afa --- /dev/null +++ b/.github/workflows/release-on-tag.yml @@ -0,0 +1,89 @@ +name: Start Modern Metals release from tag + +on: + push: + tags: + - '*.*.*.*' + +permissions: + actions: read + contents: read + +concurrency: + group: modernmetals-release-starter-${{ github.ref_name }} + cancel-in-progress: false + +jobs: + validate-release-tag: + name: Validate tag for manual release confirmation + if: github.repository == 'MinecraftModDevelopmentMods/ModernMetals' + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Check out tagged source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + fetch-depth: 0 + + - name: Validate release tag, target metadata, and prior CI + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail + + value() { sed -n "s/^$1=//p" gradle.properties; } + release_version="$(value mod_version)" + minecraft_version="$(value minecraft_version)" + loader_name="$(value loader_name)" + loader_code="$(value loader_code)" + + IFS=. read -r mc_major mc_minor mc_patch extra <<<"$minecraft_version" + mc_patch="${mc_patch:-0}" + if [[ -n "${extra:-}" || ! "$mc_major" =~ ^[0-9]+$ || ! "$mc_minor" =~ ^[0-9]+$ \ + || ! "$mc_patch" =~ ^[0-9]+$ || "$loader_name:$loader_code" != 'forge:1' ]]; then + echo "Invalid target metadata $minecraft_version/$loader_name/$loader_code" >&2 + exit 1 + fi + printf -v minor_padded '%02d' "$((10#$mc_minor))" + printf -v patch_padded '%02d' "$((10#$mc_patch))" + target_suffix="${mc_major}${minor_padded}${patch_padded}${loader_code}" + + if [[ ! "$release_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.${target_suffix}$ ]]; then + echo "mod_version $release_version does not match target $target_suffix" >&2 + exit 1 + fi + if [[ "$GITHUB_REF_NAME" != "$release_version" ]]; then + echo "Release tag must equal mod_version $release_version; found $GITHUB_REF_NAME" >&2 + exit 1 + fi + + successful_ci="$(gh api \ + "repos/$GITHUB_REPOSITORY/commits/$GITHUB_SHA/check-runs?per_page=100" \ + --jq '[.check_runs[] | select(.name == "Build, test, and audit" and .conclusion == "success")] | length')" + if [[ "$successful_ci" -lt 1 ]]; then + echo "The tagged commit has no successful Build, test, and audit check" >&2 + exit 1 + fi + + - name: Record the required manual publication step + env: + RELEASE_WORKFLOW_URL: https://github.com/${{ github.repository }}/actions/workflows/deploy-release.yml + shell: bash + run: | + { + echo "## Release candidate validated" + echo + echo "Tag \`$GITHUB_REF_NAME\` matches the selected target and has a successful Build, test, and audit check." + echo + echo "**Nothing has been published.**" + echo + echo "To continue, open [Release Modern Metals]($RELEASE_WORKFLOW_URL), select **Run workflow**, and enter:" + echo + echo "- release_version: \`$GITHUB_REF_NAME\`" + echo "- release_commit: \`$GITHUB_SHA\`" + echo "- tested_jar_sha256: SHA-256 of the tested game jar" + echo "- curseforge_release_level: \`release\`, \`beta\`, or \`alpha\`" + echo "- confirm_live_publication: \`true\`" + } >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/validate-gradle-build.yml b/.github/workflows/validate-gradle-build.yml new file mode 100644 index 00000000..cc6223df --- /dev/null +++ b/.github/workflows/validate-gradle-build.yml @@ -0,0 +1,23 @@ +name: Validate Gradle Wrapper + +on: + push: + branches: + - master-1.12 + - 'feature/**' + pull_request: + branches: + - master-1.12 + +permissions: + contents: read + +jobs: + validation: + name: Validation + runs-on: ubuntu-latest + steps: + - name: Check out source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - name: Validate wrapper integrity + uses: gradle/actions/wrapper-validation@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6 diff --git a/.gitignore b/.gitignore index bc1ca2d0..3a14b223 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ bin *.launch .settings -.settings/* .metadata .classpath .project @@ -21,14 +20,19 @@ build # other eclipse run -classes /README.txt /forge-*-changelog.txt secret.json -libs/* -!/libs/README.txt - +# Local context and generated verification +/AGENTS.md +/agent-notes/ +/.codex/ +/.verification*/ +/release-candidates/ +__pycache__/ +*.pyc .DS_Store -bugreport.md +Thumbs.db +desktop.ini diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index dd5385bb..00000000 --- a/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -language: java -os: linux -dist: trusty -addons: - apt: - update: true - packages: -# - oracle-java8-installer -git: - quiet: true -arch: amd64 -before_cache: - - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ -cache: - directories: - - $HOME/.gradle/caches/ - - $HOME/.gradle/wrapper/ -notifications: - email: false -jdk: - - oraclejdk8 -before_install: skip -install: skip -before_script: - - chmod a+x gradlew -script: - - ./gradlew setupCIWorkspace -S - - ./gradlew clean build -S diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 26da6009..e155d9d0 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1 +1,11 @@ +2.5.1.112021 for 1.12.2 + +- Updated the build to ForgeGradle 7 and moved Modern Metals to the ModDev Zone namespace. +- Switched ore generation to OreSpawn 4 and added ore overlays that use the active stone texture. +- Kept titanium ore optional. Rutile and magnesium still make titanium. +- Added a settings screen in Forge and OreSpawn. Modern Metals follows Base Metals' Fantasy Level when installed with a compatible version. +- Added damage from contact with molten metal and kept ore processing in MMDLib's shared crusher registry. +- Fixed stainless steel and nichrome blend recipes while keeping the Chrome ore-dictionary alias. +- Fixed plate and door models when Base Metals isn't installed. + *Metals family wide cleanups and bugfixes diff --git a/README.md b/README.md index c9413ec3..8a695443 100644 --- a/README.md +++ b/README.md @@ -1,75 +1,39 @@ -[![](https://img.shields.io/badge/Discord-MMD-green.svg?style=flat&logo=Discord)](https://discord.mcmoddev.com) -[![](http://cf.way2muchnoise.eu/modern-metals.svg)](https://www.curseforge.com/minecraft/mc-mods/modern-metals) -[![](http://cf.way2muchnoise.eu/versions/modern-metals.svg)](https://www.curseforge.com/minecraft/mc-mods/modern-metals) -[![Build Status](https://travis-ci.org/jriwanek/ModernMetals.svg?branch=master)](https://travis-ci.org/jriwanek/ModernMetals) - -### ModernMetals -Addon to BaseMetals supporting Modern Metals - -Content -------- -Bars - -Doors - -Ores - -Plates - -Trapdoors - -Armor - -Tools: Crackhammers, Axes, Hoes, Pickaxes, Shovels, Fishing Rods - -Ores, Blocks, Ingots & Nuggets - -Powders & Blends (Ingot and Nugget size) - -Rods - -Gears - -Weapons: Swords, Bows, Arrows, Crossbows & Bolts - -Materials ---------- -Aluminum - -Aluminum Brass - -Cadmium - -Chromium - -Iridium - -Lithium - -Magnesium - -Manganese - -Niter - -Osmium - -Phosphorous - -Plutonium - -Rutile - -Silicon - -Sulfur - -Tantalum - -Titanium - -Tungsten - -Uranium - -Zirconium +# Modern Metals for Minecraft 1.12.2 + +Modern Metals adds industrial metals and alloys, including aluminum, chromium, +osmium and titanium. It adds their ores and molten forms, equipment, building +blocks and Crack Hammer processing. + +Install Forge 14.23.5.2859, [MMDLib 1.0.0-rc2.36][mmdlib] and +[OreSpawn 4.1.3.112021][orespawn]. Modern Metals also works alongside +[Base Metals 2.6.1.112021][basemetals]. Base Metals supplies additional +ingredients for alloys that use its steel, zinc, brass or nickel. + +The Modern Metals settings are available from Forge's Mods list and OreSpawn's +Mods list. Fantasy Level comes from a compatible Base Metals installation. +When running Modern Metals by itself, set Fantasy Level in +`config/ModernMetals.cfg` or in the settings screen. Low Fantasy restricts +implausible gear and building recipes. Existing items remain usable. Restart +Minecraft after changing the setting. + +Titanium is made from rutile and magnesium by default. The optional titanium +ore rule can be enabled in OreSpawn. Existing chunks keep their ores. OreSpawn +uses its native profile for new chunks and can import supported legacy files. +Back up a world and its configs before upgrading from OreSpawn 3. + +[mmdlib]: https://www.curseforge.com/minecraft/mc-mods/mmdlib/files/2712947 +[orespawn]: https://maven.moddev.zone/releases/zone/moddev/mc/orespawn/OreSpawn/4.1.3.112021/ +[basemetals]: https://maven.moddev.zone/releases/zone/moddev/mc/basemetals/BaseMetals/2.6.1.112021/ + +## Building + +The Gradle wrapper runs on Java 17, ForgeGradle's Mavenizer uses Java 25, and +the game and compiler use Java 8. Run `./gradlew check build +verifyProjectResources writeReleaseChecksums prepareEclipse`. To stage a +local mirror first, run `python gradle/stage-legacy-dependencies.py ` +and pass `-PlegacyDependencyVerificationRepository=` to Gradle. +The runtime jar, sources and Javadocs are written to `build/libs`. + +CI builds from the pinned jars, audits the package and checks disposable Forge +servers. The release workflow takes an explicitly confirmed version, commit +and game jar checksum. The update feed should be advanced after publication. diff --git a/build.gradle b/build.gradle index 56ee5834..c82efaa5 100644 --- a/build.gradle +++ b/build.gradle @@ -1,448 +1,174 @@ -buildscript { - repositories { - jcenter() - maven { - name = 'forge' - url = 'http://files.minecraftforge.net/maven' - } - maven { - name = 'gradle' - url 'https://plugins.gradle.org/m2/' - } - maven { - name = 'sonatype' - url = 'https://oss.sonatype.org/content/groups/public' - } - } - dependencies { - classpath "net.minecraftforge.gradle:ForgeGradle:${project.fg_version}" - classpath 'org.junit.platform:junit-platform-gradle-plugin:1.2.+' -// classpath 'com.jaredsburrows:gradle-checker-framework-plugin:0.2.1' - } -} +import org.apache.tools.ant.filters.FixCrLfFilter plugins { -//id 'com.github.spotbugs' version '1.6.2' - id 'com.matthewprenger.cursegradle' version '1.4.0' - id 'org.sonarqube' version '3.0' -} - -apply plugin: 'net.minecraftforge.gradle.forge' -apply plugin: 'com.matthewprenger.cursegradle' -apply plugin: 'maven-publish' -apply plugin: 'org.sonarqube' -apply plugin: 'org.junit.platform.gradle.plugin' -apply plugin: 'jacoco' -//apply plugin: 'com.github.spotbugs' -//apply plugin: 'com.jaredsburrows.checkerframework' - -repositories { - mavenLocal() - mavenCentral() -} - -dependencies { - testCompile('org.junit.jupiter:junit-jupiter-api:5.2.+') - testCompile('org.junit.platform:junit-platform-launcher:1.2.+') - testRuntime('org.junit.jupiter:junit-jupiter-engine:5.2.+') - testCompile('org.mockito:mockito-core:2.+') -} - -project.afterEvaluate { - def junitPlatformTestTask = project.tasks.getByName('junitPlatformTest') - - // configure jacoco to analyze the junitPlatformTest task - jacoco { - // this tool version is compatible with - toolVersion = '0.8.1' - applyTo junitPlatformTestTask - } - - // create junit platform jacoco task - project.task(type: JacocoReport, "junitPlatformJacocoReport", - { - sourceDirectories = files("./src/main") - classDirectories = files("$buildDir/classes/main") - executionData junitPlatformTestTask - }) -} - -junitPlatform { - // platformVersion '1.2.0' - // logManager 'org.apache.logging.log4j.jul.LogManager' - reportsDir file("${project.buildDir}/test-results/junit-platform") - // enableStandardTestTask true - // selectors (optional) - // filters (optional) -} - -/* -spotbugs { - toolVersion = '3.1.5' -} -*/ - -tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' - options.compilerArgs = [ - '-Xlint:deprecation', // deprecation, rawtypes, cast, unchecked, all -// '-Xdiags:verbose', -// '-Werror' - ] -} - -javadoc { - failOnError = false -} - -test { -// useJUnitPlatform() -/* - useJUnitPlatform { - includeTags 'fast', 'smoke & feature-a' - // excludeTags 'slow', 'ci' - includeEngines 'junit-jupiter' - // excludeEngines 'junit-vintage' - } -*/ -} - -def mod_file = getModFile() -def short_version = getVersion('VERSION', mod_file) -version = mc_version + '-' + short_version -def display_version = short_version - -if (System.getenv().BUILD_NUMBER) { - version += '.' + System.getenv().BUILD_NUMBER - display_version += '.' + System.getenv().BUILD_NUMBER -} - -sourceCompatibility = targetCompatibility = '1.8' - -eclipse.project { - buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder' - natures 'org.eclipse.buildship.core.gradleprojectnature' -} - -class Secrets { - def data = null - - def getProperty(String key) { - return data ? data[key] : '' - } -} - -import groovy.json.JsonSlurper - -def secretFile -if (System.getenv().SECRET_FILE) { - secretFile = file System.getenv().SECRET_FILE -} else { - secretFile = file 'secret.json' -} - -project.ext.secret = new Secrets() -if (secretFile.exists()) { - secretFile.withReader { - project.ext.secret.data = new JsonSlurper().parse it - } + id 'java' + id 'eclipse' + id 'idea' + id 'maven-publish' + id 'net.minecraftforge.renamer' version '1.1.5' + id 'net.minecraftforge.gradle' version '7.0.34' +} + +group = project.mod_group +version = project.mod_version +base.archivesName = 'ModernMetals' + +if (!(project.version ==~ /\d+\.\d+\.\d+\.112021/)) { + throw new GradleException('Use Major.Minor.Patch.112021 for Minecraft 1.12.2 on Forge.') +} + +// Gradle and Mavenizer use modern JDKs; the mod and game still use Java 8. +java { + toolchain.languageVersion = JavaLanguageVersion.of(8) + withSourcesJar() + withJavadocJar() +} +tasks.withType(JavaCompile).configureEach { + options.encoding = 'UTF-8' + sourceCompatibility = targetCompatibility = '1.8' + options.compilerArgs.addAll(['-Xmaxerrs', '1000']) +} +tasks.named('compileTestJava', JavaCompile) { options.compilerArgs.add('-proc:none') } +tasks.withType(Test).configureEach { useJUnitPlatform() } +tasks.withType(Javadoc).configureEach { + failOnError = false + options.encoding = 'UTF-8' + options.addStringOption('Xdoclint:none', '-quiet') + options.addStringOption('docencoding', 'UTF-8') + options.addStringOption('charset', 'UTF-8') + options.addBooleanOption('notimestamp', true) +} +tasks.withType(AbstractArchiveTask).configureEach { + preserveFileTimestamps = false + reproducibleFileOrder = true + includeEmptyDirs = false + exclude '**/desktop.ini', '**/Thumbs.db', '**/.DS_Store' +} +// Mapped dependency bytes no longer match their original signatures. Keep the +// verified downloads intact and strip signatures only from renamed copies. +tasks.withType(net.minecraftforge.renamer.gradle.RenameJar).configureEach { + additionalArgs.addAll('--strip-sigs', 'ALL', '--disable-abstract-param') +} + +def textPatterns = ['cfg', 'css', 'html', 'info', 'java', 'js', 'json', 'lang', 'mcmeta', 'md', 'properties', 'txt', 'xml'] + .collect { "**/*.${it}".toString() } +def normalizeText = { details -> + details.filter(FixCrLfFilter, eol: FixCrLfFilter.CrLf.newInstance('lf'), + eof: FixCrLfFilter.AddAsisRemove.newInstance('asis')) +} + +// Preserve the tested game jar's line endings for this compatibility list. +def legacyApiList = 'META-INF/modernmetals-legacy-api.list' +def normalizeLegacyApi = { details -> + details.filter(FixCrLfFilter, eol: FixCrLfFilter.CrLf.newInstance('crlf'), + eof: FixCrLfFilter.AddAsisRemove.newInstance('asis')) +} + +def developmentOutput = layout.buildDirectory.dir('forge12-development/main') +def prepareDevelopmentMod = tasks.register('prepareDevelopmentMod', Sync) { + group = 'ide' + description = 'Merges production classes and processed resources for Forge 1.12 launches.' + dependsOn tasks.named('classes') + from tasks.named('compileJava', JavaCompile).flatMap { it.destinationDirectory } + from sourceSets.main.output.resourcesDir + into developmentOutput } minecraft { - version = project.forge_mc_version + '-' + project.forge_version - runDir = 'run' - mappings = project.mcp_mappings - - if (project.hasProperty('signSHA1')) { - replace '@FINGERPRINT@', project.findProperty('signSHA1') - } else { - replace '@FINGERPRINT@', '' - } - - if (project.hasProperty('coreplugin')) { - clientJvmArgs += '-Dfml.coreMods.load=' + project.coreplugin - serverJvmArgs += '-Dfml.coreMods.load=' + project.coreplugin - } - - if (project.hasProperty('mc_username')) { - clientRunArgs += ['--username', project.mc_username] - if (project.hasProperty('mc_password')) { - clientRunArgs += ['--password', project.mc_password] + mappings channel: project.mappings_channel, version: project.mappings_version + if (gradle.startParameter.offline) mavenizerArguments.add('--offline') + runs { + configureEach { + workingDir.convention layout.projectDirectory.dir( + providers.gradleProperty('modernMetalsRunDirectory').getOrElse('run')) + environment 'MC_VERSION', project.minecraft_version + environment 'MOD_CLASSES', developmentOutput.get().asFile.absolutePath + systemProperty 'fml.coreMods.load', project.coreplugin + systemProperty 'log4j.skipJansi', 'true' + systemProperty 'forge.logging.console.level', 'info' + systemProperty 'log4j.configurationFile', file('gradle/ide/log4j2.xml').toURI().toString() + if (providers.gradleProperty('modernMetalsLaunchProbePhase').isPresent()) { + systemProperty 'launch.phase', providers.gradleProperty('modernMetalsLaunchProbePhase').get() + } + } + register('client') + register('server') { args 'nogui' } } - } - if (project.hasProperty('mc_uuid')) { - clientRunArgs += ['--uuid', project.mc_uuid] - } - - // disable server gui - serverRunArgs += 'nogui' - - // skip the screen to confirm that you want to load a world with missing registry entries - serverJvmArgs += '-Dfml.doNotBackup=true' - clientJvmArgs += '-Dfml.doNotBackup=true' - - // skip having to confirm on server - serverJvmArgs += '-Dfml.queryResult=confirm' - - //skip jansi warnings in the log - serverJvmArgs += '-Dlog4j.skipJansi=true' - clientJvmArgs += '-Dlog4j.skipJansi=true' - - if (project.hasProperty('client_args')) { - clientJvmArgs += project.client_args - } - if (project.hasProperty('server_args')) { - serverJvmArgs += project.server_args - } -} - -repositories { - maven { - name 'MMD' - url 'https://maven.mcmoddev.com/' - } - maven { // CoFH - name 'Covers Maven' - url 'http://maven.covers1624.net/' - } - maven { // Mantle, TCon, JEI - name 'DVS1 Maven FS' - url 'http://dvs1.progwml6.com/files/maven/' - } - maven { - name = 'CurseForge' - url = 'https://minecraft.curseforge.com/api/maven/' - } -} - -dependencies { - deobfProvided "com.mcmoddev:AdditionalLootTables:${mc_version}-${alt_version}" - deobfProvided "com.mcmoddev:OreSpawn:${mc_version}-${orespawn_version}" - deobfProvided "com.mcmoddev:MMDLib:${mc_version}-${mmdlib_version}" - - deobfProvided ("slimeknights.mantle:Mantle:${mc_version}-${mantle_version}") { - exclude group: 'mezz.jei' - } - - deobfProvided ("slimeknights:TConstruct:${mc_version}.2-${tconstruct_version}") { - exclude group: 'mezz.jei' - } - - deobfProvided ("constructs-armory:conarm:${mc_version}.2:${conarm_version}") { - exclude group: 'mezz.jei' - } - - // CoFHCore for TE integration - deobfProvided ("cofh:CoFHCore:${mc_version}-${cofhcore_version}:universal") { - exclude group: 'mezz.jei' - } -} - -processResources { - inputs.property 'version', project.version - inputs.property 'mcversion', project.minecraft.version - - from (sourceSets.main.resources.srcDirs) { - include 'mcmod.info' - expand 'version': short_version, 'mcversion': project.minecraft.version - } - - from (sourceSets.main.resources.srcDirs) { - exclude 'mcmod.info' - } - - rename '(.+_at.cfg)', 'META-INF/$1' } -jar { - manifest { - if (project.hasProperty('coreplugin')) { - attributes 'FMLCorePluginContainsFMLMod': 'true' - attributes 'FMLCorePlugin': project.coreplugin +tasks.matching { it.name in ['runClient', 'runServer'] }.configureEach { + dependsOn prepareDevelopmentMod + dependsOn 'prepareJava8Launcher' + doFirst { + Set splitOutputs = sourceSets.main.output.files.collect { it.canonicalFile } as Set + classpath = files(developmentOutput, tasks.named('prepareJava8Launcher'), classpath.filter { + !splitOutputs.contains(it.canonicalFile) && it.name != 'slime-launcher-0.2.2.jar' + }) + environment 'MC_VERSION', project.minecraft_version + environment 'MOD_CLASSES', developmentOutput.get().asFile.absolutePath } -// attributes 'FMLAT' : '' - } -} - -task apiJar(type: Jar) { - classifier = 'apiClasses' - from apiClasses -} - -task deobfJar(type: Jar) { - classifier = 'deobf' - from sourceSets.main.output -} - -task signJar(type: SignJar, dependsOn: reobfJar) { - // Skips if the keyStore property is missing. - onlyIf { - project.hasProperty('keyStore') - } - - // findProperty allows us to reference the property without it existing. - // Using project.propName would cause the script to fail validation if - // the property did not exist. - keyStore = project.findProperty('keyStore') - alias = project.findProperty('keyStoreAlias') - storePass = project.findProperty('keyStorePass') - keyPass = project.findProperty('keyStoreKeyPass') - inputFile = jar.archivePath - outputFile = jar.archivePath -} - -build.dependsOn signJar - -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.getDestinationDir() } -artifacts { - if (project.hasProperty('doAPIJar')) { - archives apiJar - } - if (project.hasProperty('doDeobfJar')) { - archives deobfJar - } - if (project.hasProperty('doSourceJar')) { - archives sourceJar - } - if (project.hasProperty('doJavadocJar')) { - archives javadocJar - } -} +def legacyMirror = file(providers.gradleProperty('legacyDependencyVerificationRepository') + .orElse(providers.gradleProperty('legacyDependencyRepository')) + .getOrElse('.gradle/legacy-dependencies')) +def legacyRepository = { String remote -> legacyMirror.isDirectory() ? legacyMirror.toURI() : uri(remote) } -publishing { - publications { - mavenJava(MavenPublication) { - groupId project.group - artifactId project.archivesBaseName - version project.version - from components.java - if (project.hasProperty('doSourceJar')) { - artifact sourceJar { - classifier 'sources' - } - } - if (project.hasProperty('doAPIJar')) { - artifact apiJar { - classifier 'api' - } - } - if (project.hasProperty('doDeobfJar')) { - artifact deobfJar { - classifier 'deobf' - } - } - if (project.hasProperty('doJavadocJar')) { - artifact javadocJar { - classifier 'javadoc' - } - } - } - } - repositories { +repositories { + minecraft.mavenizer(it) + maven fg.forgeMaven + maven fg.minecraftLibsMaven maven { - credentials { - username secret.username - password secret.password - } - url secret.url + name = 'VerifiedLegacyMirror' + url = legacyMirror.toURI() + metadataSources { artifact() } } - } -} - -curseforge { - if (project.hasProperty('cf_project_id') && project.hasProperty('cf_release_type') && project.hasProperty('cf_changelog_filename') && - ((project.cf_release_type == 'alpha') || (project.cf_release_type == 'beta') || (project.cf_release_type == 'release'))) { - apiKey = secret.curseforgeAPIKey - project { - id = project.cf_project_id - changelog = file(project.cf_changelog_filename) - releaseType = project.cf_release_type - addGameVersion(project.mc_version) - mainArtifact(jar) { - displayName = "$project.description $display_version" - relations { - requiredLibrary 'mmd-orespawn' - requiredLibrary 'mmdlib' - optionalLibrary 'additional-loot-tables' - } - } - if (project.hasProperty('doAPIJar')) { - addArtifact(apiJar) { - displayName = "$project.description $display_version API" - } - } - if (project.hasProperty('doSourceJar')) { - addArtifact(sourceJar) { - displayName = "$project.description $display_version Sources" - } - } - if (project.hasProperty('doDeobfJar')) { - addArtifact(deobfJar) { - displayName = "$project.description $display_version Development" - } - } - if (project.hasProperty('doJavadocJar')) { - addArtifact(javadocJar) { - displayName = "$project.description $display_version Javadoc" - } - } + exclusiveContent { + forRepository { maven { url = legacyRepository('https://www.cursemaven.com'); metadataSources { artifact() } } } + filter { includeGroup 'curse.maven' } } - } -} - -sonarqube { - properties { - property 'sonar.host.url', secret.sonarHost - property 'sonar.organization', secret.sonarOrganization - property 'sonar.login', secret.sonarToken - // property 'sonar.projectName', project.archivesBaseName - // property 'sonar.projectKey', "$project.group:$project.archivesBaseName" - // property 'sonar.branch.name', 'master-1.12' - // property 'sonar.branch.target', '' - property 'sonar.junit.reportPaths', "${project.buildDir}/test-results/junit-platform/*.xml" - property 'sonar.jacoco.reportPaths', "${project.buildDir}/jacoco/junitPlatformTest.exec" - } -} - -String getModFile() { - String path = '' - FileTree tree = fileTree(dir: 'src/main/java') - tree.include '**/*.java' - tree.visit { element -> - if (element.file.isFile()) { - element.file.eachLine { String s -> - s = s.trim() - if (s.startsWith('@Mod(')) { - path = "src/main/java/$element.relativePath" - } - } + exclusiveContent { + forRepository { maven { url = legacyRepository('https://maven.moddev.zone/releases'); metadataSources { artifact() } } } + filter { includeGroup 'zone.moddev.mc.basemetals'; includeGroup 'zone.moddev.mc.orespawn' } } - } - return path -} - -String getVersion(String type, String mod_file) { - String major = '0' - String revision = '0' - String patch = '0' - File file = file(mod_file) - def prefix = ~/^(?:public|protected|private) static final String $type = ".*$/ - file.eachLine { String s -> - s = s.trim() - if (s ==~ prefix || prefix.matcher(s).matches()) { - String[] bits = s.split("=") - String interesting = bits[1].trim() - interesting = interesting.substring(1, interesting.length() - 2) - String[] pts = interesting.trim().split("\\.") - - major = pts[0] ?: '0' - revision = pts[1] ?: '0' - patch = pts[2] ?: '0' + mavenCentral() +} + +apply from: 'gradle/dependencies.gradle' +renamer.mappings.from minecraft.dependency.toSrgFile + +tasks.named('processResources', ProcessResources) { + filteringCharset = 'UTF-8' + exclude '**/desktop.ini', '**/Thumbs.db', '**/.DS_Store' + inputs.property 'version', project.version + inputs.property 'mcversion', project.minecraft_version + filesMatching('mcmod.info') { expand version: project.version, mcversion: project.minecraft_version } + rename '(.+_at.cfg)', 'META-INF/$1' + filesMatching('**/*.lang') { details -> details.name = details.name.toLowerCase(Locale.ROOT) } + filesMatching(textPatterns, normalizeText) + filesMatching(legacyApiList, normalizeLegacyApi) +} + +tasks.named('jar', Jar) { + archiveClassifier = 'deobf' + destinationDirectory = layout.buildDirectory.dir('libs-dev') + manifest.attributes( + 'Implementation-Title': 'ModernMetals', 'Implementation-Version': project.version, + 'Maven-Artifact': "${project.group}:ModernMetals:${project.version}", + 'Built-On-Java': '8', 'Built-On': "${project.minecraft_version}-${project.forge_version}", + 'FMLCorePluginContainsFMLMod': 'true', 'FMLCorePlugin': project.coreplugin) +} +['sourcesJar', 'javadocJar'].each { name -> + tasks.named(name, Jar) { + filteringCharset = 'UTF-8' + filesMatching(textPatterns, normalizeText) + filesMatching(legacyApiList, normalizeLegacyApi) } - } - return "$major.$revision.$patch" } + +ext.modernMetalsBuild = [developmentOutput: developmentOutput].asImmutable() +apply from: 'gradle/ore-models.gradle' +apply from: 'gradle/release/artifacts.gradle' +apply from: 'gradle/release/publishing.gradle' +apply from: 'gradle/verification/runtime.gradle' +apply from: 'gradle/ide/eclipse.gradle' diff --git a/gradle.properties b/gradle.properties index 9d62a18e..6b585402 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,57 +1,33 @@ -org.gradle.daemon = false +org.gradle.jvmargs=-Xmx3G +org.gradle.daemon=false +org.gradle.configuration-cache=false +org.gradle.caching=true +org.gradle.parallel=false +net.minecraftforge.gradle.merge-source-sets=false -group = com.mcmoddev -archivesBaseName = ModernMetals -name = ModernMetals -description = Modern Metals +minecraft_version=1.12.2 +forge_version=14.23.5.2859 +mappings_channel=stable +mappings_version=39-1.12 -mc_version = 1.12 +mod_id=modernmetals +mod_group=zone.moddev.mc.modernmetals +mod_version=2.5.1.112021 +mod_artifact=ModernMetals +loader_name=forge +loader_code=1 +java_version=8 +java_toolchain_version=8.0.502+7 +java_setup_version=8.0.502+7 +gradle_java_version=17 +gradle_java_setup_version=17.0.1+12 +mavenizer_java_version=25.0.3+9 +curseforge_project_id=244357 +cf_requirements=mmd-orespawn,mmdlib +orespawn_curse_project_id=245586 +coreplugin=zone.moddev.mc.modernmetals.compat.ModernMetalsLoadingPlugin +orespawn_version=4.1.3.112021 +orespawn_version_range=[4.1.3.112021,5.0.0) -fg_version = 2.3-SNAPSHOT -forge_mc_version = 1.12.2 -forge_version = 14.23.5.2768 -mcp_mappings = stable_39 - -cf_project_id = 244357 -cf_release_type = release -cf_changelog_filename = CHANGELOG.txt - -// coreplugin = - -doSourceJar = true -// doAPIJar -doDeobfJar = true -doJavadocJar = true - -ccl_version=3.1.3.+ -ccc_version=2.4.1.+ -nei_version=2.4.0.+ -jei_version=4.7.5.+ -mantle_version=1.3.2.+ -tconstruct_version=2.10.1.87 -conarm_version=1.2.0 -mcmp_version=2.3.3 -mcmp_experimental_version=2.0.1_31 -top_version=1.4.18-10 -alt_version=2.0.3.39 -orespawn_version=3.2.+ -tesla_version=1.0.63 -bge_version=2.5.0-beta+ -bme_version=2.5.0-beta+ -bmi_version=2.5.0-beta+ -fme_version=2.5.0-beta+ -mme_version=2.5.0-beta+ -mmdlib_version=1.0.0-rc+ -cofhcore_version=4.3.6.+ -mekanism_version=9efd716ea0 -ic2_version=2.8.73-ex112 -minetweaker_version=3.+ -mtlib_version=3.+ -ct_version=4.1.9.+ -ct_api_version=4.1.9.+ -hwyla_version=1.8.23-B38_1.12 -veinminer_version=1.12-0.38.2.647+b31535a -endercore_version=+ -enderio_version=+ -thermal_foundation_version=+ -thermal_expansion_version=+ +mmdlib_version=1.0.0-rc2.36 +forge_installer_sha256=DBBA1CD1BF34A4B82148BAC50A6150F63FFCB2D5DC7D88A0508C27FDCFF15B76 diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle new file mode 100644 index 00000000..f6e2d664 --- /dev/null +++ b/gradle/dependencies.gradle @@ -0,0 +1,65 @@ +import groovy.json.JsonSlurper +import java.security.MessageDigest + +def specs = new JsonSlurper().parse(file('gradle/dependencies.json')) +def mapped = specs.collectEntries { [(it.id): renamer.dependency(it.coordinate)] } + +configurations { + legacyModArtifacts { canBeConsumed = false; transitive = false } + java8LauncherArtifact { canBeConsumed = false; transitive = false } +} +dependencies { + implementation minecraft.dependency("net.minecraftforge:forge:${minecraft_version}-${forge_version}") + java8LauncherArtifact 'net.minecraftforge:slime-launcher:0.2.2' + compileOnly 'org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209' + specs.each { spec -> + if (spec.runtime) implementation mapped[spec.id] + else compileOnly mapped[spec.id] + legacyModArtifacts spec.coordinate + } + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.2' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.2' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.10.2' +} + +// Forge 1.12's mod scanner cannot read Java 11 multi-release entries. They are +// unused on Java 8, so omit them from the development launcher copy. +tasks.register('prepareJava8Launcher', Jar) { + dependsOn configurations.java8LauncherArtifact + archiveFileName = 'slime-launcher-0.2.2-java8.jar' + destinationDirectory = layout.buildDirectory.dir('development-dependencies') + from { zipTree(configurations.java8LauncherArtifact.singleFile) } + exclude 'META-INF/versions/**', 'META-INF/MANIFEST.MF', 'META-INF/*.SF', 'META-INF/*.RSA', 'META-INF/*.DSA' +} + +// Verify the original files, not renamed copies whose bytes depend on mappings. +tasks.register('verifyReleaseDependencies') { + group = 'verification' + description = 'Checks every pinned integration API against its SHA-256.' + inputs.file 'gradle/dependencies.json' + doLast { + def artifacts = configurations.legacyModArtifacts.resolvedConfiguration.resolvedArtifacts + specs.each { spec -> + def parts = spec.coordinate.split(':') + def artifact = artifacts.find { it.moduleVersion.id.group == parts[0] + && it.name == parts[1] && it.moduleVersion.id.version == parts[2] } + if (artifact == null) throw new GradleException("Missing ${spec.coordinate}") + String actual = MessageDigest.getInstance('SHA-256').digest(artifact.file.bytes).encodeHex().toString() + if (!actual.equalsIgnoreCase(spec.sha256)) { + throw new GradleException("Checksum mismatch for ${spec.coordinate}: ${actual}") + } + if (spec.id == 'orespawn') { + new java.util.jar.JarFile(artifact.file).withCloseable { jar -> + def metadata = new JsonSlurper().parse(jar.getInputStream(jar.getJarEntry('mcmod.info'))) + if (!metadata.any { it.modid == 'orespawn' && it.version == project.orespawn_version } + || jar.getJarEntry('zone/moddev/mc/orespawn/api/OreSpawnApi.class') == null + || jar.manifest.mainAttributes.getValue('OreSpawn-API-Version') != '1') { + throw new GradleException('Wrong OreSpawn identity or public API.') + } + } + } + } + } +} +tasks.named('compileJava') { dependsOn tasks.named('verifyReleaseDependencies') } +tasks.named('check') { dependsOn tasks.named('verifyReleaseDependencies') } diff --git a/gradle/dependencies.json b/gradle/dependencies.json new file mode 100644 index 00000000..5982e876 --- /dev/null +++ b/gradle/dependencies.json @@ -0,0 +1,58 @@ +[ + { + "id": "mmdlib", + "coordinate": "curse.maven:mmdlib-261744:2712947", + "repository": "https://www.cursemaven.com", + "sha256": "FE2229E6755A5FD306C33CC22DCCF055378D371339133E4912C5DC9213E360AF", + "runtime": true + }, + { + "id": "orespawn", + "coordinate": "zone.moddev.mc.orespawn:OreSpawn:4.1.3.112021", + "repository": "https://maven.moddev.zone/releases", + "sha256": "5B5929CCDEB055679FD6D7B9629BF94F06085A6ECD791FD08D0500F666C079DD", + "runtime": true + }, + { + "id": "basemetals", + "coordinate": "zone.moddev.mc.basemetals:BaseMetals:2.6.1.112021", + "repository": "https://maven.moddev.zone/releases", + "sha256": "FBAE982E308DCC1AC4281A5372560D06C8BB3A027EE290EDA4AC6875F5680FDC", + "runtime": false + }, + { + "id": "alt", + "coordinate": "curse.maven:additional-loot-tables-243157:2691613", + "repository": "https://www.cursemaven.com", + "sha256": "D5661DBF99859E8C9CF29704AB0FD4A132D062031A4023A0FF5E6F9D06ACE3E2", + "runtime": false + }, + { + "id": "mantle", + "coordinate": "curse.maven:tinkers-mantle-74924:2550550", + "repository": "https://www.cursemaven.com", + "sha256": "7758817445D55B65581A34A906EE01155C4598FDDF06E7D72944C840A1105184", + "runtime": false + }, + { + "id": "tconstruct", + "coordinate": "curse.maven:tinkers-construct-74072:2571785", + "repository": "https://www.cursemaven.com", + "sha256": "B1E21352C7BCEC95E33769308D9E70CC83E8F3A97C27FB94F4F0F55CE3CF96F2", + "runtime": false + }, + { + "id": "conarm", + "coordinate": "curse.maven:constructs-armory-287683:2622725", + "repository": "https://www.cursemaven.com", + "sha256": "C1EB6F2BB127652D8FDEAC21FF4BF376F17804E2A5B8EDF14C7C082497AF58A1", + "runtime": false + }, + { + "id": "cofhcore", + "coordinate": "curse.maven:cofh-core-69162:2920433", + "repository": "https://www.cursemaven.com", + "sha256": "E07B0C48DEF9CE8CFAA4CF7A7C656BF7F7D6815F74FBCC2957DD43EFA78486A9", + "runtime": false + } +] diff --git a/gradle/ide/eclipse.gradle b/gradle/ide/eclipse.gradle new file mode 100644 index 00000000..842c8ca1 --- /dev/null +++ b/gradle/ide/eclipse.gradle @@ -0,0 +1,206 @@ +def escapeXml = { String text -> + text.replace('&', '&').replace('"', '"').replace('<', '<').replace('>', '>') +} + +eclipse { + project { + buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder' + natures 'org.eclipse.buildship.core.gradleprojectnature' + } + classpath { + downloadSources = true + downloadJavadoc = true + file.beforeMerged { classpath -> + Set managed = ['src/main/java', 'src/main/resources', 'src/test/java', + 'build/resources/main', 'tools/validation/legacy111', + 'tools/validation/legacy111/migration-probe', + 'tools/validation/legacy111/launch-probe', + 'tools/validation/legacy111/orespawn-probe'] as Set + classpath.entries.removeAll { it.kind == 'src' && managed.contains(it.path) } + } + file.whenMerged { classpath -> + // Eclipse must copy the expanded metadata, not the source placeholders. + classpath.entries.findAll { it.kind == 'src' && it.path == 'src/main/resources' }.each { + it.path = 'build/resources/main' + } + classpath.entries.findAll { it.kind == 'src' && !it.path.startsWith('src/main/') }.each { + if (it.path != 'build/resources/main') it.entryAttributes['test'] = 'true' + } + } + file.withXml { provider -> + Node root = provider.asNode() + root.classpathentry.findAll { it.@kind == 'lib' }.each { root.remove(it) } + if (!root.classpathentry.any { it.@path == 'org.eclipse.buildship.core.gradleclasspathcontainer' }) { + root.appendNode('classpathentry', [kind: 'con', path: 'org.eclipse.buildship.core.gradleclasspathcontainer']) + } + } + } + synchronizationTasks 'genEclipseRuns' +} +idea.module { downloadSources = true; downloadJavadoc = true } + +def configureBuildship = tasks.register('configureEclipseBuildship') { + group = 'ide' + doLast { + File target = file('.settings/org.eclipse.buildship.core.prefs') + Properties values = new Properties() + if (target.isFile()) target.withInputStream { values.load(it) } + [ + 'eclipse.preferences.version': '1', + 'connection.gradle.distribution': 'GRADLE_DISTRIBUTION(WRAPPER)', + 'connection.gradle.user.home': gradle.gradleUserHomeDir.canonicalPath, + 'gradle.user.home': gradle.gradleUserHomeDir.canonicalPath, + 'connection.java.home': System.getProperty('java.home'), + 'java.home': System.getProperty('java.home'), + 'connection.project.dir': '', 'override.workspace.settings': 'true' + ].each { key, value -> values.setProperty(key, value) } + target.parentFile.mkdirs() + target.withOutputStream { values.store(it, 'Generated by configureEclipseBuildship.') } + } +} + +tasks.register('isolateEclipseProductionRuns') { + group = 'ide' + description = 'Quotes launcher paths and keeps optional API jars and tests out of ordinary launches.' + dependsOn configureBuildship, tasks.named('processResources'), tasks.named('prepareJava8Launcher') + doLast { + // Copy, rather than Sync: Eclipse owns and compiles the classes in bin/main. + copy { from layout.buildDirectory.dir('resources/main'); into file('bin/main') } + file('run').mkdirs() + List runtime = configurations.runtimeClasspath.files.collect { it.canonicalFile } + ['runClient.launch', 'runServer.launch'].each { name -> + File launch = file(name) + if (!launch.isFile()) throw new GradleException("Missing generated launch: ${name}") + String text = launch.getText('UTF-8').replace('\r\n', '\n') + text = text.replace(escapeXml(configurations.java8LauncherArtifact.singleFile.canonicalPath), + escapeXml(tasks.named('prepareJava8Launcher', Jar).get().archiveFile.get().asFile.canonicalPath)) + def arguments = text =~ /()/ + if (arguments.count != 1) throw new GradleException("Malformed launcher arguments: ${name}") + String args = arguments[0][2] + ['cache', 'metadata', 'to-srg', 'to-obf'].each { flag -> + int start = args.indexOf("--${flag} ") + if (start < 0) throw new GradleException("Missing --${flag} in ${name}") + start += flag.length() + 3 + int end = args.indexOf(' --', start) + if (end < 0) throw new GradleException("Unterminated --${flag} in ${name}") + String value = args.substring(start, end) + if (!value.startsWith('"')) args = args.substring(0, start) + '"' + value + '"' + args.substring(end) + } + text = text.replace(arguments[0][0], arguments[0][1] + args + arguments[0][3]) + text = text.replaceAll(//, + '') + String modClasses = '' + if (text.contains('key="MOD_CLASSES"')) { + text = text.replaceAll(//, + java.util.regex.Matcher.quoteReplacement(modClasses)) + } else { + text = text.replace('', ' ' + modClasses + '\n ') + } + List lines = text.split('\n', -1) as List + def entries = (0.. + String memento = '' + ' ' + }.join('\n') + text = lines.join('\n') + if (!text.contains('ATTR_EXCLUDE_TEST_CODE')) { + text = text.replace('', + ' \n') + } + launch.setText(text, 'UTF-8') + } + } +} +tasks.named('genEclipseRuns') { finalizedBy tasks.named('isolateEclipseProductionRuns') } +tasks.named('eclipse') { dependsOn tasks.named('genEclipseRuns') } + +def verifyJavaSourceLayout = tasks.register('verifyJavaSourceLayout') { + group = 'verification' + description = 'Checks that Java package names match their source folders, as Eclipse requires.' + inputs.files(provider { sourceSets.collect { it.java } }) + doLast { + List mismatches = [] + sourceSets.each { sourceSet -> + sourceSet.java.visit { entry -> + if (entry.directory || !entry.name.endsWith('.java')) return + + String path = entry.relativePath.pathString + int separator = path.lastIndexOf('/') + String expected = separator < 0 ? '' : path.substring(0, separator).replace('/', '.') + def declaration = entry.file.getText('UTF-8') =~ /(?m)^\s*package\s+([\w.]+)\s*;/ + String declared = declaration.find() ? declaration.group(1) : '' + + if (declared != expected) { + mismatches.add("${sourceSet.name}: ${path} declares '${declared}', expected '${expected}'") + } + } + } + if (!mismatches.isEmpty()) { + throw new GradleException('Java packages do not match their source folders:\n' + mismatches.join('\n')) + } + } +} + +tasks.named('check') { dependsOn verifyJavaSourceLayout } + +tasks.register('verifyEclipseProductionClasspath') { + group = 'verification' + dependsOn tasks.named('genEclipseRuns'), verifyJavaSourceLayout + mustRunAfter tasks.named('eclipse') + doLast { + List runtime = configurations.runtimeClasspath.files.collect { it.canonicalFile } + Set optional = configurations.compileClasspath.files.collect { it.canonicalFile } as Set + optional.removeAll(runtime) + ['runClient.launch', 'runServer.launch'].each { name -> + String text = file(name).getText('UTF-8').replace('\\', '/') + if (!text.contains('MAIN_TYPE" value="net.minecraftforge.launcher.Main"') + || !text.contains('StandardVMType/JavaSE-8') + || !text.contains('MC_VERSION" value="1.12.2"') + || !text.contains('ATTR_EXCLUDE_TEST_CODE') + || text.contains('gradleclasspathcontainer') || text.contains('bin/test') + || text.contains('slime-launcher-0.2.2.jar') + || text.contains('junit-') || text.contains('legacy111-probe')) { + throw new GradleException("Unsafe production launch: ${name}") + } + if (runtime.any { !text.contains(it.path.replace('\\', '/')) } + || optional.any { text.contains(it.path.replace('\\', '/')) }) { + throw new GradleException("Runtime/compile-only separation failed in ${name}") + } + ['cache', 'metadata', 'to-srg', 'to-obf'].each { flag -> + if (!text.contains("--${flag} "")) throw new GradleException("Unquoted --${flag} in ${name}") + } + } + String info = file('bin/main/mcmod.info').getText('UTF-8') + if (info.contains('${version}') || !info.contains(project.version.toString())) { + throw new GradleException('Eclipse is using unprocessed resources.') + } + def classpath = new groovy.xml.XmlSlurper().parse(file('.classpath')) + List sourcePaths = classpath.classpathentry.findAll { it.@kind == 'src' }.collect { it.@path.text() } + if (sourcePaths.size() != sourcePaths.unique(false).size() + || sourcePaths.contains('tools/validation/legacy111')) { + throw new GradleException('Eclipse retains stale or duplicate source-folder entries.') + } + classpath.classpathentry.findAll { it.@kind == 'src' && !it.@path.text().startsWith('src/main/') + && it.@path.text() != 'build/resources/main' }.each { + if (!it.attributes.attribute.any { it.@name == 'test' && it.@value == 'true' }) { + throw new GradleException("Non-production Eclipse source is not marked as test code: ${it.@path}") + } + } + } +} +tasks.register('prepareEclipse') { + group = 'ide' + description = 'Generates Eclipse metadata and verifies Java 8 production-only launches.' + dependsOn tasks.named('eclipse'), tasks.named('verifyEclipseProductionClasspath'), 'writeEclipseCompileClasspath' +} + +tasks.register('writeEclipseCompileClasspath') { + group = 'verification' + description = 'Writes the resolved editor classpath for headless Eclipse compiler checks.' + doLast { + File target = layout.buildDirectory.file('ide/compile-classpath.txt').get().asFile + target.parentFile.mkdirs() + target.setText(configurations.compileClasspath.asPath, 'UTF-8') + } +} diff --git a/gradle/ide/log4j2.xml b/gradle/ide/log4j2.xml new file mode 100644 index 00000000..a770e981 --- /dev/null +++ b/gradle/ide/log4j2.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/gradle/ore-models.gradle b/gradle/ore-models.gradle new file mode 100644 index 00000000..cd503373 --- /dev/null +++ b/gradle/ore-models.gradle @@ -0,0 +1,71 @@ +import groovy.json.JsonOutput + +// Forge 1.12 has no GatherDataEvent. Generate the equivalent model resources +// before processResources, without launching Minecraft or writing into src. +def assets = layout.projectDirectory.dir('src/main/resources/assets/modernmetals') +def overlayFiles = fileTree(assets.dir('textures/blocks/ore_overlays')) { include '*_ore.png' } +def oreStates = fileTree(assets.dir('blockstates')) { include '*_ore.json' } +def generated = layout.buildDirectory.dir('generated/ore-models') + +def generateOreModels = tasks.register('generateOreModels') { + group = 'build' + description = 'Generates the Forge 1.12 ore models from KiriCattus’s overlays.' + inputs.files(overlayFiles, oreStates).withPathSensitivity(PathSensitivity.RELATIVE) + outputs.dir(generated) + + doLast { + def names = overlayFiles.files.collect { it.name - '.png' }.sort() + def expected = oreStates.files.collect { it.name - '.json' }.findAll { it != 'thorium_ore' }.sort() + if (names != expected) { + throw new GradleException("Ore overlays do not match ore blockstates: missing ${expected - names}, extra ${names - expected}") + } + + def dir = generated.get().dir('assets/modernmetals/models/block').asFile + dir.mkdirs() + def writeModel = { String name, Map model -> + new File(dir, "${name}.json").setText(JsonOutput.prettyPrint(JsonOutput.toJson(model)) + '\n', 'UTF-8') + } + def cube = { Number from, Number to, String texture -> + def faces = ['down', 'up', 'north', 'south', 'west', 'east'].collectEntries { face -> + [(face): [texture: texture, cullface: face, uv: [0, 0, 16, 16]]] + } + [from: [from, from, from], to: [to, to, to], faces: faces] + } + // Same two cubes and offset as Base Metals, with the legacy texture paths. + writeModel('ore_overlay', [ + parent: 'minecraft:block/block', + elements: [cube(0, 16, '#base'), cube(-0.05, 16.05, '#overlay')] + ]) + names.each { name -> + writeModel(name, [ + parent: 'modernmetals:block/ore_overlay', + textures: [particle: 'minecraft:blocks/stone', base: 'minecraft:blocks/stone', + overlay: "modernmetals:blocks/ore_overlays/${name}"] + ]) + } + // Clean up only obsolete ore JSON files in this task's own output directory. + dir.listFiles().findAll { it.isFile() && it.name.endsWith('_ore.json') && !(it.name - '.json' in names) }.each { + if (!it.delete()) throw new GradleException("Could not remove stale generated ore model ${it.name}") + } + } +} + +tasks.named('processResources', ProcessResources) { + dependsOn generateOreModels + from generated + duplicatesStrategy = DuplicatesStrategy.FAIL + doFirst { + // Copy tasks can leave the old flat textures behind after an incremental upgrade. + overlayFiles.files.each { overlay -> + def flat = new File(destinationDir, "assets/modernmetals/textures/blocks/${overlay.name}") + if (flat.isFile() && !flat.delete()) { + throw new GradleException("Could not remove obsolete processed texture ${flat.name}") + } + } + } +} + +tasks.named('sourcesJar', Jar) { + dependsOn generateOreModels + from generated +} diff --git a/gradle/prepare-packaged-forge-runtime.sh b/gradle/prepare-packaged-forge-runtime.sh new file mode 100644 index 00000000..016bea12 --- /dev/null +++ b/gradle/prepare-packaged-forge-runtime.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ $# -ne 2 ]]; then + echo "usage: $0 " >&2 + exit 2 +fi + +runtime="$1" +java8="$2" +version="1.12.2-14.23.5.2859" +installer="${runtime}/forge-${version}-installer.jar" +expected="$(sed -n 's/^forge_installer_sha256=//p' gradle.properties)" + +if [[ -e "$runtime" ]] && [[ -n "$(find "$runtime" -mindepth 1 -maxdepth 1 -print -quit)" ]]; then + echo "Packaged Forge destination must be empty: $runtime" >&2 + exit 1 +fi +mkdir -p "$runtime" + +curl --fail --location --silent --show-error \ + "https://maven.minecraftforge.net/net/minecraftforge/forge/${version}/forge-${version}-installer.jar" \ + --output "$installer" +actual="$(sha256sum "$installer" | awk '{print toupper($1)}')" +if [[ "$actual" != "$expected" ]]; then + echo "Forge installer SHA-256 is $actual; expected $expected" >&2 + exit 1 +fi + +"$java8" -jar "$installer" --installServer "$runtime" +installed="$runtime/forge-${version}.jar" +universal="$runtime/forge-${version}-universal.jar" +test -f "$installed" +cp "$installed" "$universal" +test -f "$runtime/forge-${version}-universal.jar" +test -f "$runtime/minecraft_server.1.12.2.jar" +test -d "$runtime/libraries" diff --git a/gradle/release/artifacts.gradle b/gradle/release/artifacts.gradle new file mode 100644 index 00000000..4b4030ff --- /dev/null +++ b/gradle/release/artifacts.gradle @@ -0,0 +1,185 @@ +import groovy.json.JsonSlurper +import java.security.MessageDigest +import java.util.jar.Manifest +import java.util.zip.ZipFile + +def releaseJar = renamer.classes(tasks.named('jar', Jar)) { + map.from minecraft.dependency.toSrgFile + archiveClassifier = null + output = layout.buildDirectory.file("libs/ModernMetals-${project.version}.jar") +} +['apiElements', 'runtimeElements'].each { name -> + configurations.named(name) { artifacts.clear() } + artifacts.add(name, releaseJar) +} +tasks.named('assemble') { dependsOn releaseJar, tasks.named('sourcesJar'), tasks.named('javadocJar') } + +tasks.register('verifyJava8Toolchain') { + group = 'verification' + description = 'Checks Java 8 compatibility; exact compiler versions are required only for release builds.' + doLast { + def compiler = javaToolchains.compilerFor { languageVersion = JavaLanguageVersion.of(8) }.get().metadata + if (compiler.languageVersion.asInt() != 8) throw new GradleException('Minecraft 1.12 requires Java 8.') + if (providers.gradleProperty('releaseBuild').getOrElse('false').toBoolean() + && (compiler.javaRuntimeVersion != '1.8.0_502-b07' + || System.getProperty('java.runtime.version') != '17.0.1+12')) { + throw new GradleException('Release builds require Temurin 8u502-b07 and Gradle on 17.0.1+12.') + } + if (providers.gradleProperty('releaseBuild').getOrElse('false').toBoolean()) { + def mavenizer = javaToolchains.launcherFor { languageVersion = JavaLanguageVersion.of(25) }.get().metadata + if (mavenizer.javaRuntimeVersion != '25.0.3+9-LTS') { + throw new GradleException('Release setup requires Mavenizer on Temurin 25.0.3+9.') + } + } + } +} + +tasks.register('verifyReleaseConfiguration') { + group = 'verification' + dependsOn tasks.named('verifyJava8Toolchain') + doLast { + if (project.group != 'zone.moddev.mc.modernmetals' || project.version != '2.5.1.112021' + || project.minecraft_version != '1.12.2' || project.forge_version != '14.23.5.2859' + || project.mappings_channel != 'stable' || project.mappings_version != '39-1.12') { + throw new GradleException('Modern Metals target, mappings or Maven identity changed.') + } + String source = file('src/main/java/zone/moddev/mc/modernmetals/ModernMetals.java').getText('UTF-8') + if (!source.contains('VERSION = "' + project.version + '"') || !source.contains('MODID = "modernmetals"')) { + throw new GradleException('The mod identity differs from the build metadata.') + } + if (project.orespawn_version != '4.1.3.112021' + || project.orespawn_version_range != '[4.1.3.112021,5.0.0)' + || !source.contains('required-after:orespawn@' + project.orespawn_version_range) + || !source.contains('required-after:forge@[14.23.5.2859,)')) { + throw new GradleException('The OreSpawn or Forge runtime requirement changed.') + } + if (!source.contains('required-after:mmdlib@[1.0.0-rc2,2.0.0)') + || source.contains('required-after:basemetals')) { + throw new GradleException('Modern Metals requires the standalone MMDLib. Base Metals remains optional.') + } + } +} + +def expectedFiles = ['', '-sources', '-javadoc'].collect { "ModernMetals-${project.version}${it}.jar".toString() }.asImmutable() +def preparedDirectory = providers.gradleProperty('preparedReleaseDir') +def auditJars = { File directory -> + ['','-sources','-javadoc'].each { suffix -> + File jar = new File(directory, "ModernMetals-${project.version}${suffix}.jar") + if (!jar.isFile() || jar.length() == 0) throw new GradleException("Missing ${jar.name}") + new ZipFile(jar).withCloseable { zip -> + zip.entries().each { entry -> + String path = entry.name.toLowerCase(Locale.ROOT) + if (path.startsWith('review/') || path.contains('probe') || path.contains('fixture') + || path.contains('agent-notes') || path.contains('agents.md') || path.contains('.codex') + || path.contains('.agents') || path.contains('claude') || path.contains('gemini') + || path.tokenize('/').any { it in ['desktop.ini', 'thumbs.db', '.ds_store'] } + || path.startsWith('tools/') || path.startsWith('reference/') + || path.startsWith('com/mcmoddev/lib/') || path.startsWith('zone/moddev/mc/basemetals/') || path.startsWith('zone/moddev/mc/mmdlib/') + || path.startsWith('com/mcmoddev/orespawn/') + || path == 'assets/modernmetals/orespawn/modernmetals.json' + || path.startsWith('org/junit/') || path.startsWith('net/minecraft/')) { + throw new GradleException("Non-production entry in ${jar.name}: ${entry.name}") + } + def date = entry.timeLocal + if (!entry.directory && !(date.toString() in ['1980-01-01T00:00', '1980-02-01T00:00'])) { + throw new GradleException("Volatile archive timestamp: ${entry.name}") + } + if (!entry.directory && path ==~ /.*\.(java|html|css|js|json|lang|mcmeta|md|txt|xml|cfg|info|properties)/) { + def decoder = java.nio.charset.StandardCharsets.UTF_8.newDecoder() + .onMalformedInput(java.nio.charset.CodingErrorAction.REPORT) + .onUnmappableCharacter(java.nio.charset.CodingErrorAction.REPORT) + String content = decoder.decode(java.nio.ByteBuffer.wrap(zip.getInputStream(entry).bytes)).toString() + if (path != 'meta-inf/modernmetals-legacy-api.list' && content.contains('\r')) throw new GradleException("Non-LF text in ${entry.name}") + } + if (!entry.directory && path ==~ /.*\.(class|java|html|json|info|xml|properties|txt|md|lang|cfg)/) { + String content = new String(zip.getInputStream(entry).bytes, + java.nio.charset.StandardCharsets.ISO_8859_1) + if (content.contains(project.projectDir.absolutePath) + || content.contains('/home/runner/work/')) { + throw new GradleException("Local build path in ${entry.name}") + } + } + if (entry.name.endsWith('.class')) { + byte[] data = zip.getInputStream(entry).bytes + int major = ((data[6] & 255) << 8) | (data[7] & 255) + if (major != 52) throw new GradleException("Not Java 8 bytecode: ${entry.name}") + String constants = new String(data, java.nio.charset.StandardCharsets.ISO_8859_1) + if (constants.contains(project.projectDir.absolutePath)) { + throw new GradleException("Local path in ${entry.name}") + } + } + } + if (suffix == '') { + def manifest = new Manifest(zip.getInputStream(zip.getEntry('META-INF/MANIFEST.MF'))).mainAttributes + if (manifest.getValue('Maven-Artifact') != "${project.group}:ModernMetals:${project.version}" + || manifest.getValue('FMLCorePlugin') != project.coreplugin + || manifest.keySet().any { it.toString().toLowerCase().contains('timestamp') }) { + throw new GradleException('Incorrect release manifest.') + } + def info = new JsonSlurper().parse(zip.getInputStream(zip.getEntry('mcmod.info')))[0] + if (info.modid != 'modernmetals' || info.version != project.version || info.mcversion != '1.12.2') { + throw new GradleException('Incorrect processed mcmod.info.') + } + if (zip.getEntry('zone/moddev/mc/modernmetals/ModernMetals.class') == null + || zip.getEntry('META-INF/modernmetals-legacy-api.list') == null) { + throw new GradleException('Missing mod entry point or legacy API map.') + } + } + } + } +} + +tasks.register('verifyReleaseArtifacts') { + group = 'verification' + description = 'Audits the shipped jars for Java 8 bytecode, metadata and test isolation.' + dependsOn tasks.named('assemble'), tasks.named('verifyReleaseConfiguration'), tasks.named('verifyReleaseDependencies') + doLast { auditJars(layout.buildDirectory.dir('libs').get().asFile) } +} + +tasks.register('writeReleaseChecksums') { + group = 'verification' + dependsOn tasks.named('verifyReleaseArtifacts') + doLast { + File directory = layout.buildDirectory.dir('libs').get().asFile + List lines = ['','-sources','-javadoc'].collect { suffix -> + File jar = new File(directory, "ModernMetals-${project.version}${suffix}.jar") + MessageDigest.getInstance('SHA-256').digest(jar.bytes).encodeHex().toString() + ' ' + jar.name + } + File sums = layout.buildDirectory.file('release/SHA256SUMS').get().asFile + sums.parentFile.mkdirs() + sums.setText(lines.join('\n') + '\n', 'UTF-8') + } +} +tasks.named('check') { dependsOn tasks.named('verifyReleaseConfiguration') } + +tasks.register('verifyPreparedReleaseArtifacts') { + group = 'verification' + description = 'Verifies a previously built bundle without rebuilding its jars.' + doLast { + if (!preparedDirectory.isPresent()) throw new GradleException('Set preparedReleaseDir to the immutable release bundle.') + File directory = file(preparedDirectory.get()) + Set expected = (expectedFiles + ['SHA256SUMS', 'CHANGELOG.txt']) as Set + if (!directory.isDirectory() || directory.listFiles().collect { it.name }.toSet() != expected) { + throw new GradleException('The prepared release must contain exactly three jars, SHA256SUMS and CHANGELOG.txt.') + } + List lines = new File(directory, 'SHA256SUMS').readLines('UTF-8') + if (lines.size() != 3) throw new GradleException('The prepared checksum list must contain exactly three files.') + Set checked = [] as Set + lines.each { line -> + def match = line =~ /^([a-fA-F0-9]{64}) (ModernMetals-[^\/\\]+\.jar)$/ + if (!match.matches() || !expectedFiles.contains(match[0][2]) || !checked.add(match[0][2])) { + throw new GradleException('Invalid or duplicate prepared checksum entry.') + } + File jar = new File(directory, match[0][2]) + String actual = MessageDigest.getInstance('SHA-256').digest(jar.bytes).encodeHex().toString() + if (!actual.equalsIgnoreCase(match[0][1])) throw new GradleException("Prepared checksum mismatch: ${jar.name}") + } + if (!new File(directory, 'CHANGELOG.txt').getText('UTF-8').startsWith(project.version.toString() + ' for 1.12.2')) { + throw new GradleException('The prepared changelog belongs to a different release.') + } + auditJars(directory) + } +} + +ext.modernMetalsRelease = [jar: releaseJar, expectedFiles: expectedFiles, + preparedDirectory: preparedDirectory].asImmutable() diff --git a/gradle/release/publishing.gradle b/gradle/release/publishing.gradle new file mode 100644 index 00000000..ce2b8dbe --- /dev/null +++ b/gradle/release/publishing.gradle @@ -0,0 +1,96 @@ +import groovy.xml.XmlSlurper + +def releaseJar = project.ext.modernMetalsRelease.jar +def expectedReleaseFiles = project.ext.modernMetalsRelease.expectedFiles +def preparedReleaseDir = project.ext.modernMetalsRelease.preparedDirectory + +// A normal build does not publish anything; publishing needs credentials. +def mavenUploadUrl = providers.environmentVariable('MAVEN_UPLOAD_URL') + .map { it.trim() }.filter { !it.isEmpty() } + .orElse('https://invalid.invalid/missing-maven-upload-url') +def mavenUploadUsername = providers.environmentVariable('MAVEN_UPLOAD_USERNAME') +def mavenUploadPassword = providers.environmentVariable('MAVEN_UPLOAD_PASSWORD') + +publishing { + publications { + mavenJava(MavenPublication) { + groupId = project.group.toString() + artifactId = base.archivesName.get() + version = project.version.toString() + if (preparedReleaseDir.isPresent()) { + File prepared = file(preparedReleaseDir.get()) + artifact(new File(prepared, expectedReleaseFiles[0])) + artifact(new File(prepared, expectedReleaseFiles[1])) { classifier = 'sources' } + artifact(new File(prepared, expectedReleaseFiles[2])) { classifier = 'javadoc' } + } else { + artifact(releaseJar) + artifact(tasks.named('sourcesJar')) + artifact(tasks.named('javadocJar')) + } + pom { + name = 'Modern Metals' + description = 'Metals, alloys, equipment and Crack Hammer processing for Minecraft 1.12.2.' + url = 'https://github.com/MinecraftModDevelopmentMods/ModernMetals' + licenses { + license { + name = 'GNU Lesser General Public License, Version 2.1' + url = 'https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt' + } + } + scm { + connection = 'scm:git:https://github.com/MinecraftModDevelopmentMods/ModernMetals.git' + developerConnection = 'scm:git:ssh://git@github.com/MinecraftModDevelopmentMods/ModernMetals.git' + url = 'https://github.com/MinecraftModDevelopmentMods/ModernMetals' + } + } + } + } + repositories { + maven { + name = 'release' + url = uri(mavenUploadUrl.get()) + credentials { + username = mavenUploadUsername.orNull ?: '' + password = mavenUploadPassword.orNull ?: '' + } + } + } +} + +tasks.register('validateMavenReleaseCredentials') { + group = 'publishing' + doLast { + if (!providers.environmentVariable('MAVEN_UPLOAD_URL').getOrElse('').trim() + || !mavenUploadUsername.getOrElse('').trim() || !mavenUploadPassword.getOrElse('').trim()) { + throw new GradleException('MAVEN_UPLOAD_URL, MAVEN_UPLOAD_USERNAME, and MAVEN_UPLOAD_PASSWORD are required') + } + URI target = uri(providers.environmentVariable('MAVEN_UPLOAD_URL').get()) + if (target.scheme != 'https' || !target.host || target.host == 'invalid.invalid' + || !mavenUploadUsername.get().trim() || !mavenUploadPassword.get().trim()) { + throw new GradleException('Maven release publication requires an HTTPS repository and non-empty credentials') + } + } +} + +tasks.withType(PublishToMavenRepository).configureEach { + dependsOn tasks.named('validateMavenReleaseCredentials') + dependsOn 'verifyMavenCoordinates' + if (preparedReleaseDir.isPresent()) { + dependsOn tasks.named('verifyPreparedReleaseArtifacts') + } else { + dependsOn tasks.named('verifyReleaseArtifacts') + } +} + +tasks.register('verifyMavenCoordinates') { + group = 'verification' + dependsOn tasks.named('generatePomFileForMavenJavaPublication') + doLast { + def pom = new XmlSlurper(false, false).parse(layout.buildDirectory.file('publications/mavenJava/pom-default.xml').get().asFile) + def actual = [pom.groupId.text(), pom.artifactId.text(), pom.version.text()] + def expected = ['zone.moddev.mc.modernmetals', 'ModernMetals', project.version.toString()] + if (actual != expected) throw new GradleException("Wrong Maven coordinate: ${actual.join(':')}") + } +} + +tasks.named('check') { dependsOn tasks.named('verifyMavenCoordinates') } diff --git a/gradle/stage-legacy-dependencies.py b/gradle/stage-legacy-dependencies.py new file mode 100644 index 00000000..48030759 --- /dev/null +++ b/gradle/stage-legacy-dependencies.py @@ -0,0 +1,53 @@ +"""Build a checksum-verified Maven mirror from downloads or an existing Gradle cache.""" + +import argparse +import hashlib +import json +import time +import urllib.error +import urllib.request +from pathlib import Path + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("destination", type=Path) + parser.add_argument("--cache", type=Path, help="Existing Gradle user home; matching files are verified before reuse") + args = parser.parse_args() + specs = json.loads(Path(__file__).with_name("dependencies.json").read_text(encoding="utf-8")) + + for spec in specs: + group, artifact, version, *classifier = spec["coordinate"].split(":") + filename = f"{artifact}-{version}" + ("-" + classifier[0] if classifier else "") + ".jar" + relative = Path(group.replace(".", "/")) / artifact / version / filename + target = args.destination.resolve() / relative + target.parent.mkdir(parents=True, exist_ok=True) + + if not target.exists(): + cached = list((args.cache / "caches/modules-2/files-2.1" / group / artifact / version).glob("*/" + filename)) if args.cache else [] + if cached: + data = cached[0].read_bytes() + else: + url = spec["repository"].rstrip("/") + "/" + relative.as_posix() + request = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"}) + for attempt in range(3): + try: + with urllib.request.urlopen(request, timeout=30) as response: + data = response.read() + break + except (OSError, urllib.error.URLError): + if attempt == 2: + raise + print(f"Retrying {spec['coordinate']}", flush=True) + time.sleep(2) + if hashlib.sha256(data).hexdigest().upper() != spec["sha256"]: + raise ValueError(f"Checksum mismatch: {spec['coordinate']}") + target.write_bytes(data) + + if hashlib.sha256(target.read_bytes()).hexdigest().upper() != spec["sha256"]: + raise ValueError(f"Checksum mismatch in mirror: {target}") + print(spec["id"], spec["sha256"], flush=True) + + +if __name__ == "__main__": + main() diff --git a/gradle/stage-legacy-dependencies.sh b/gradle/stage-legacy-dependencies.sh new file mode 100644 index 00000000..530f8933 --- /dev/null +++ b/gradle/stage-legacy-dependencies.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ $# -ne 1 ]]; then + echo "usage: $0 " >&2 + exit 2 +fi + +"${PYTHON:-python3}" "$(dirname "$0")/stage-legacy-dependencies.py" "$1" diff --git a/gradle/verification/runtime.gradle b/gradle/verification/runtime.gradle new file mode 100644 index 00000000..24ac44c8 --- /dev/null +++ b/gradle/verification/runtime.gradle @@ -0,0 +1,35 @@ +tasks.register('verifyProjectResources', Exec) { + group = 'verification' + dependsOn tasks.named('processResources') + commandLine providers.gradleProperty('pythonExecutable').getOrElse('python3'), + 'tools/validation/legacy112/audit_resources.py', 'build/resources/main' +} + +sourceSets { + runtimeProbe { + java.srcDirs = ['tools/validation/legacy112/runtime-probe'] + compileClasspath = sourceSets.main.compileClasspath + sourceSets.main.output + } +} + +def probe = tasks.register('runtimeProbeJar', Jar) { + archiveFileName = 'modernmetals-probe.jar' + destinationDirectory = layout.buildDirectory.dir('test-mods') + from sourceSets.runtimeProbe.output +} +def packagedProbe = renamer.classes(probe) { + map.from minecraft.dependency.toSrgFile + output = layout.buildDirectory.file('test-mods/modernmetals-probe-packaged.jar') +} +tasks.register('packagedForgeRuntimeTest', Exec) { + group = 'verification' + dependsOn 'verifyReleaseArtifacts', packagedProbe + doFirst { + def launcher = javaToolchains.launcherFor { languageVersion = JavaLanguageVersion.of(8) }.get() + commandLine providers.gradleProperty('pythonExecutable').getOrElse('python3'), + 'tools/validation/legacy112/run_packaged_suite.py', '--java', launcher.executablePath.asFile.absolutePath, + '--template', file(providers.gradleProperty('packagedForgeRuntimeRoot').get()).absolutePath, + '--output', file(providers.gradleProperty('packagedForgeRuntimeOutput').getOrElse('build/packaged-forge-checks')).absolutePath, + '--mirror', file(providers.gradleProperty('legacyDependencyVerificationRepository').getOrElse('.gradle/legacy-dependencies')).absolutePath + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 0d4a9516871afd710a9d84d89e31ba77745607bd..b1b8ef56b44f16b14dc800fa8103a6d89abb526f 100644 GIT binary patch literal 48462 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 R2np~f1o-$>Q}y+){|A@R9n$~+ literal 54413 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a95009c3..dbe66e1d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,10 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip +distributionSha256Sum=9c0f7faeeb306cb14e4279a3e084ca6b596894089a0638e68a07c945a32c9e14 +distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip +networkTimeout=10000 +retries=0 +retryBackOffMs=500 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index cccdd3d5..249efbb0 100755 --- a/gradlew +++ b/gradlew @@ -1,78 +1,128 @@ -#!/usr/bin/env sh +#!/bin/sh + +# +# Copyright © 2015 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# gradlew start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh gradlew +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -81,92 +131,118 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" fi +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index f9553162..a51ec4f5 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,84 +1,82 @@ -@if "%DEBUG%" == "" @echo off +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem -@rem Gradle startup script for Windows +@rem gradlew startup script for Windows @rem @rem ########################################################################## -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal +@rem Set local scope for the variables, and ensure extensions are enabled +setlocal EnableExtensions set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* +"%COMSPEC%" /c exit 1 :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 -:mainEnd -if "%OS%"=="Windows_NT" endlocal +@rem Execute gradlew +@rem endlocal doesn't take effect until after the line is parsed and variables are expanded +@rem which allows us to clear the local environment before executing the java command +endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel -:omega +:exitWithErrorLevel +@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts +"%COMSPEC%" /c exit %ERRORLEVEL% diff --git a/settings.gradle b/settings.gradle index 0e64f4a2..c4091f15 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,5 @@ -rootProject.name = archivesBaseName -enableFeaturePreview('STABLE_PUBLISHING') +plugins { + id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' +} + +rootProject.name = 'ModernMetals' diff --git a/src/main/java/com/mcmoddev/modernmetals/init/Recipes.java b/src/main/java/com/mcmoddev/modernmetals/init/Recipes.java deleted file mode 100644 index 14c12deb..00000000 --- a/src/main/java/com/mcmoddev/modernmetals/init/Recipes.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.mcmoddev.modernmetals.init; - -import com.mcmoddev.lib.data.SharedStrings; -import com.mcmoddev.lib.init.Materials; -import com.mcmoddev.lib.material.MMDMaterial; -import com.mcmoddev.modernmetals.data.MaterialNames; - -/** - * - * @author Jasmine Iwanek - * - */ -public class Recipes extends com.mcmoddev.lib.init.Recipes { - - private Recipes() { - throw new IllegalAccessError(SharedStrings.NOT_INSTANTIABLE); - } - - /** - * - */ - public static void init() { - initModSpecificRecipes(); - } - - private static void initModSpecificRecipes() { - if (Materials.hasMaterial(MaterialNames.ALUMINUM)) { - final MMDMaterial aluminum = Materials.getMaterialByName(MaterialNames.ALUMINUM); - - addAdditionalOredicts(aluminum, "Aluminium"); - addAdditionalOredicts(aluminum, "Bauxite"); - } - - if (Materials.hasMaterial(MaterialNames.ALUMINUM_BRASS)) { - final MMDMaterial aluminumBrass = Materials.getMaterialByName(MaterialNames.ALUMINUM_BRASS); - - addAdditionalOredicts(aluminumBrass, "AluminumBrass"); - addAdditionalOredicts(aluminumBrass, "Aluminiumbrass"); - addAdditionalOredicts(aluminumBrass, "AluminiumBrass"); - addAdditionalOredicts(aluminumBrass, "Alubrass"); - addAdditionalOredicts(aluminumBrass, "AluBrass"); - } - - if (Materials.hasMaterial(MaterialNames.CHROMIUM)) { - addAdditionalOredicts(MaterialNames.CHROMIUM, "Chrome"); - } - - if (Materials.hasMaterial(MaterialNames.GALVANIZED_STEEL)) { - final MMDMaterial galvanizedSteel = Materials.getMaterialByName(MaterialNames.GALVANIZED_STEEL); - - addAdditionalOredicts(galvanizedSteel, "GalvinizedSteel"); - } - - if (Materials.hasMaterial(MaterialNames.NICHROME)) { - final MMDMaterial nichrome = Materials.getMaterialByName(MaterialNames.NICHROME); - - addAdditionalOredicts(nichrome, "Nichrome"); - } - - if (Materials.hasMaterial(MaterialNames.STAINLESS_STEEL)) { - final MMDMaterial stainlessSteel = Materials.getMaterialByName(MaterialNames.STAINLESS_STEEL); - - addAdditionalOredicts(stainlessSteel, "StainlessSteel"); - } - - if (Materials.hasMaterial(MaterialNames.TUNGSTEN)) { - addAdditionalOredicts(MaterialNames.TUNGSTEN, "Wolfram"); - } - } -} diff --git a/src/main/java/com/mcmoddev/modernmetals/init/VillagerTrades.java b/src/main/java/com/mcmoddev/modernmetals/init/VillagerTrades.java deleted file mode 100644 index f30a5e6e..00000000 --- a/src/main/java/com/mcmoddev/modernmetals/init/VillagerTrades.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.mcmoddev.modernmetals.init; - -import com.mcmoddev.lib.data.SharedStrings; - -/** - * - * @author Jasmine Iwanek - * - */ -public class VillagerTrades extends com.mcmoddev.lib.init.VillagerTrades { - private VillagerTrades() { - throw new IllegalAccessError(SharedStrings.NOT_INSTANTIABLE); - } - - /** - * - */ - public static void init() { - registerCommonTrades(); - } -} diff --git a/src/main/java/com/mcmoddev/modernmetals/proxy/CommonProxy.java b/src/main/java/com/mcmoddev/modernmetals/proxy/CommonProxy.java deleted file mode 100644 index b6f5cad5..00000000 --- a/src/main/java/com/mcmoddev/modernmetals/proxy/CommonProxy.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.mcmoddev.modernmetals.proxy; - -import java.util.HashSet; - -import com.mcmoddev.lib.data.SharedStrings; -import com.mcmoddev.lib.oregen.FallbackGenerator; -import com.mcmoddev.lib.util.Config.Options; -import com.mcmoddev.modernmetals.data.MaterialNames; -import com.mcmoddev.modernmetals.init.ItemGroups; -import com.mcmoddev.modernmetals.init.Recipes; -import com.mcmoddev.modernmetals.init.VillagerTrades; -import com.mcmoddev.modernmetals.util.MMeConfig; - -import net.minecraftforge.fml.common.Loader; -import net.minecraftforge.fml.common.MissingModsException; -import net.minecraftforge.fml.common.event.FMLInitializationEvent; -import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; -import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; -import net.minecraftforge.fml.common.registry.GameRegistry; -import net.minecraftforge.fml.common.versioning.ArtifactVersion; -import net.minecraftforge.fml.common.versioning.DefaultArtifactVersion; - -/** - * Modern Metals Common Proxy. - * - * @author Jasmine Iwanek - * - */ -public class CommonProxy { - - /** - * - * @param event The Event. - */ - @SuppressWarnings("deprecation") - public void preInit(final FMLPreInitializationEvent event) { - - if ((Options.requireMMDOreSpawn()) && (!Loader.isModLoaded(SharedStrings.ORESPAWN_MODID))) { - if (Options.fallbackOrespawn()) { - GameRegistry.registerWorldGenerator(new FallbackGenerator(), 0); - } else { - final HashSet orespawnMod = new HashSet<>(); - orespawnMod.add(new DefaultArtifactVersion(SharedStrings.ORESPAWN_VERSION)); - throw new MissingModsException(orespawnMod, SharedStrings.ORESPAWN_MODID, - SharedStrings.ORESPAWN_MISSING_TEXT); - } - } - - VillagerTrades.init(); - } - - /** - * - * @param event The Event. - */ - public void init(final FMLInitializationEvent event) { - Recipes.init(); - - ItemGroups.setupIcons(MaterialNames.ALUMINUM); - } - - /** - * - * @param event The Event. - */ - public void postInit(FMLPostInitializationEvent event) { - MMeConfig.postInit(); - } -} diff --git a/src/main/java/com/mcmoddev/orespawn/ModernMetalsOreSpawn.java b/src/main/java/com/mcmoddev/orespawn/ModernMetalsOreSpawn.java deleted file mode 100644 index f94f81c9..00000000 --- a/src/main/java/com/mcmoddev/orespawn/ModernMetalsOreSpawn.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.mcmoddev.orespawn; - -import com.mcmoddev.modernmetals.ModernMetals; -import com.mcmoddev.orespawn.api.os3.OS3API; -import com.mcmoddev.orespawn.api.plugin.IOreSpawnPlugin; -import com.mcmoddev.orespawn.api.plugin.OreSpawnPlugin; - -@OreSpawnPlugin(modid = ModernMetals.MODID, resourcePath = "orespawn") -public class ModernMetalsOreSpawn implements IOreSpawnPlugin { - - @Override - public void register(final OS3API apiInterface) { - // nothing for us to do - all of our ores are in the - // jar and the code handles that - } -} diff --git a/src/main/java/com/mcmoddev/modernmetals/ModernMetals.java b/src/main/java/zone/moddev/mc/modernmetals/ModernMetals.java similarity index 74% rename from src/main/java/com/mcmoddev/modernmetals/ModernMetals.java rename to src/main/java/zone/moddev/mc/modernmetals/ModernMetals.java index f9a8a720..34a2b367 100644 --- a/src/main/java/com/mcmoddev/modernmetals/ModernMetals.java +++ b/src/main/java/zone/moddev/mc/modernmetals/ModernMetals.java @@ -1,10 +1,11 @@ -package com.mcmoddev.modernmetals; +package zone.moddev.mc.modernmetals; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import com.mcmoddev.modernmetals.proxy.CommonProxy; -import com.mcmoddev.modernmetals.util.MMeConfig; +import zone.moddev.mc.modernmetals.proxy.CommonProxy; +import zone.moddev.mc.modernmetals.content.ContentPolicy; +import zone.moddev.mc.modernmetals.util.MMeConfig; import com.mcmoddev.lib.data.SharedStrings; import net.minecraft.launchwrapper.Launch; @@ -28,10 +29,10 @@ modid = ModernMetals.MODID, name = ModernMetals.NAME, version = ModernMetals.VERSION, - dependencies = "required-after:forge@[14.21.1.2387,);required-after:mmdlib;after:tconstruct;after:conarm;before:buildingbricks", - acceptedMinecraftVersions = "[1.12,)", - certificateFingerprint = "@FINGERPRINT@", - updateJSON = ModernMetals.UPDATEJSON) + dependencies = "required-after:forge@[14.23.5.2859,);required-after:mmdlib@[1.0.0-rc2,2.0.0);required-after:orespawn@[4.1.3.112021,5.0.0);after:basemetals;after:tconstruct;after:conarm;before:buildingbricks", + acceptedMinecraftVersions = "[1.12.2]", + updateJSON = ModernMetals.UPDATEJSON, + guiFactory = "zone.moddev.mc.modernmetals.client.ModernMetalsGuiFactory") public class ModernMetals { @Instance @@ -48,11 +49,11 @@ public class ModernMetals { * increased whenever a change is made that has the potential to break * compatibility with other mods that depend on this one. */ - protected static final String VERSION = "2.5.0-rc2"; + protected static final String VERSION = "2.5.1.112021"; protected static final String UPDATEJSON = SharedStrings.UPDATE_JSON_URL + "ModernMetals/master/update.json"; - private static final String PROXY_BASE = SharedStrings.MMD_PROXY_GROUP + MODID + SharedStrings.DOT_PROXY_DOT; + private static final String PROXY_BASE = "zone.moddev.mc.modernmetals.proxy."; @SidedProxy(clientSide = PROXY_BASE + SharedStrings.CLIENTPROXY, serverSide = PROXY_BASE + SharedStrings.SERVERPROXY) @@ -75,6 +76,7 @@ public void onFingerprintViolation(FMLFingerprintViolationEvent event) { @EventHandler public static void constructing(final FMLConstructionEvent event) { MMeConfig.init(); + ContentPolicy.initialize(); } /** @@ -83,22 +85,18 @@ public static void constructing(final FMLConstructionEvent event) { */ @EventHandler public static void preInit(final FMLPreInitializationEvent event) { - ModernMetals.logger.fatal("FMLPreInitializationEvent"); proxy.preInit(event); -// MinecraftForge.EVENT_BUS.register(com.mcmoddev.modernmetals.init.Items.class); - //MinecraftForge.EVENT_BUS.register(com.mcmoddev.modernmetals.init.Blocks.class); } @EventHandler public static void init(final FMLInitializationEvent event) { - ModernMetals.logger.fatal("FMLInitializationEvent"); + zone.moddev.mc.modernmetals.worldgen.ModernMetalsWorldgenProvider.enqueue(); proxy.init(event); } @EventHandler public static void postInit(final FMLPostInitializationEvent event) { - ModernMetals.logger.fatal("FMLPostInitializationEvent"); proxy.postInit(event); } } diff --git a/src/main/java/zone/moddev/mc/modernmetals/client/ModernMetalsConfigScreen.java b/src/main/java/zone/moddev/mc/modernmetals/client/ModernMetalsConfigScreen.java new file mode 100644 index 00000000..3914de2b --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/client/ModernMetalsConfigScreen.java @@ -0,0 +1,36 @@ +package zone.moddev.mc.modernmetals.client; + +import java.util.ArrayList; +import java.util.List; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; +import net.minecraftforge.common.config.ConfigElement; +import net.minecraftforge.common.config.Configuration; +import net.minecraftforge.fml.client.config.GuiConfig; +import net.minecraftforge.fml.client.config.IConfigElement; +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.content.ContentPolicy; +import zone.moddev.mc.modernmetals.util.MMeConfig; + +/** Shared configuration screen for the Forge and OreSpawn mod lists. */ +public final class ModernMetalsConfigScreen extends GuiConfig { + public ModernMetalsConfigScreen(final GuiScreen parent) { + super(parent, elements(), ModernMetals.MODID, false, true, + I18n.format("config.modernmetals.title"), + I18n.format(ContentPolicy.inheritsBaseMetals() + ? "config.modernmetals.inherited_mode" : "config.modernmetals.description")); + } + + private static List elements() { + final Configuration config = MMeConfig.getConfiguration(); + final List elements = new ArrayList<>( + new ConfigElement(config.getCategory("General")).getChildElements()); + if (ContentPolicy.inheritsBaseMetals()) { + elements.removeIf(element -> "contentMode".equals(element.getName())); + } + for (final String category : MMeConfig.MATERIAL_CATEGORIES) { + elements.add(new ConfigElement(config.getCategory(category))); + } + return elements; + } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/client/ModernMetalsGuiFactory.java b/src/main/java/zone/moddev/mc/modernmetals/client/ModernMetalsGuiFactory.java new file mode 100644 index 00000000..ecf8054c --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/client/ModernMetalsGuiFactory.java @@ -0,0 +1,32 @@ +package zone.moddev.mc.modernmetals.client; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; +import net.minecraftforge.fml.client.IModGuiFactory; + +import java.util.Collections; +import java.util.Set; + +/** Opens the existing configuration screen from Forge's Mods list. */ +public final class ModernMetalsGuiFactory implements IModGuiFactory { + + @Override + public void initialize(final Minecraft minecraftInstance) { + // The screen uses the configuration already loaded by Modern Metals. + } + + @Override + public boolean hasConfigGui() { + return true; + } + + @Override + public GuiScreen createConfigGui(final GuiScreen parentScreen) { + return new ModernMetalsConfigScreen(parentScreen); + } + + @Override + public Set runtimeGuiCategories() { + return Collections.emptySet(); + } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/compat/LegacyNamespaceTransformer.java b/src/main/java/zone/moddev/mc/modernmetals/compat/LegacyNamespaceTransformer.java new file mode 100644 index 00000000..0c3635d6 --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/compat/LegacyNamespaceTransformer.java @@ -0,0 +1,89 @@ +package zone.moddev.mc.modernmetals.compat; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import net.minecraft.launchwrapper.IClassTransformer; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.commons.Remapper; +import org.objectweb.asm.commons.RemappingClassAdapter; + +/** Relinks existing MMD add-ons to the same classes and registries as Modern Metals. */ +public final class LegacyNamespaceTransformer implements IClassTransformer { + + private static final String OLD = "com/mcmoddev/modernmetals/"; + private static final String CURRENT = "zone/moddev/mc/modernmetals/"; + private static final Set EXPORTS = loadExports(); + + @Override + public byte[] transform(final String name, final String transformedName, final byte[] bytes) { + if (bytes == null || name == null || name.startsWith("zone.moddev.mc.modernmetals.") + || !(contains(bytes, OLD) || contains(bytes, OLD.replace('/', '.')))) { + return bytes; + } + + final ClassWriter output = new ClassWriter(0); + final Remapper names = new Remapper() { + @Override + public String map(final String internalName) { + return currentName(internalName); + } + + @Override + public Object mapValue(final Object value) { + if (value instanceof String) { + final String text = (String) value; + final String internal = text.replace('.', '/'); + final String mapped = currentName(internal); + if (mapped.equals(internal)) return value; + return text.indexOf('/') < 0 ? mapped.replace('/', '.') : mapped; + } + return super.mapValue(value); + } + }; + + new ClassReader(bytes).accept(new RemappingClassAdapter(output, names), 0); + return output.toByteArray(); + } + + private static String currentName(final String name) { + if (!name.startsWith(OLD)) return name; + + final String suffix = name.substring(OLD.length()); + final int nested = suffix.indexOf('$'); + final String owner = nested < 0 ? suffix : suffix.substring(0, nested); + return EXPORTS.contains(owner) ? CURRENT + suffix : name; + } + + private static Set loadExports() { + final Set result = new HashSet<>(); + final InputStream stream = LegacyNamespaceTransformer.class.getClassLoader() + .getResourceAsStream("META-INF/modernmetals-legacy-api.list"); + if (stream == null) throw new IllegalStateException("Missing Modern Metals API name list"); + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + if (!line.isEmpty() && !line.startsWith("#")) result.add(line); + } + } catch (final IOException failure) { + throw new IllegalStateException("Cannot read Modern Metals API names", failure); + } + return Collections.unmodifiableSet(result); + } + + private static boolean contains(final byte[] bytes, final String text) { + for (int offset = 0; offset <= bytes.length - text.length(); offset++) { + int index = 0; + while (index < text.length() && bytes[offset + index] == text.charAt(index)) index++; + if (index == text.length()) return true; + } + return false; + } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/compat/ModernMetalsLoadingPlugin.java b/src/main/java/zone/moddev/mc/modernmetals/compat/ModernMetalsLoadingPlugin.java new file mode 100644 index 00000000..45021ae0 --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/compat/ModernMetalsLoadingPlugin.java @@ -0,0 +1,19 @@ +package zone.moddev.mc.modernmetals.compat; + +import java.util.Map; +import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; + +/** Relinks old add-on names without installing a second Base Metals transformer. */ +@IFMLLoadingPlugin.Name("ModernMetals") +@IFMLLoadingPlugin.MCVersion("1.12.2") +@IFMLLoadingPlugin.SortingIndex(1002) +@IFMLLoadingPlugin.TransformerExclusions("zone.moddev.mc.modernmetals.compat.") +public final class ModernMetalsLoadingPlugin implements IFMLLoadingPlugin { + @Override public String[] getASMTransformerClass() { + return new String[] { "zone.moddev.mc.modernmetals.compat.LegacyNamespaceTransformer" }; + } + @Override public String getModContainerClass() { return null; } + @Override public String getSetupClass() { return null; } + @Override public void injectData(final Map data) { } + @Override public String getAccessTransformerClass() { return null; } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/content/ContentModeNetwork.java b/src/main/java/zone/moddev/mc/modernmetals/content/ContentModeNetwork.java new file mode 100644 index 00000000..bbe36aec --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/content/ContentModeNetwork.java @@ -0,0 +1,64 @@ +package zone.moddev.mc.modernmetals.content; + +import java.util.Set; + +import net.minecraft.network.NetworkManager; +import net.minecraft.network.play.server.SPacketDisconnect; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.network.FMLNetworkEvent.CustomPacketRegistrationEvent; +import net.minecraftforge.fml.common.network.NetworkRegistry; + +/** Advertise the active recipe mode in Forge's login channel registration. */ +public final class ContentModeNetwork { + private static final String HIGH = "ModernMetals|High"; + private static final String LOW = "ModernMetals|Low"; + + private ContentModeNetwork() {} + + public static void register() { + NetworkRegistry.INSTANCE.newEventDrivenChannel(ContentPolicy.active().lowFantasy() ? LOW : HIGH); + MinecraftForge.EVENT_BUS.register(new ContentModeNetwork()); + } + + @SubscribeEvent + public void checkMode(final CustomPacketRegistrationEvent event) { + final Set channels = event.getRegistrations(); + // Forge includes FML|HS in its login registration, not ordinary channel updates. + if (!"REGISTER".equals(event.getOperation()) || !channels.contains("FML|HS")) return; + + final boolean localLow = ContentPolicy.active().lowFantasy(); + final boolean remoteLow = channels.contains(LOW); + final boolean remoteHigh = channels.contains(HIGH); + final boolean server = event.getSide().isServer(); + final ITextComponent reason; + if (remoteLow == remoteHigh) { + // A client with no mode channel may also lack the translation key. + reason = server + ? new TextComponentString("Modern Metals could not check the Fantasy Level. " + + "Use the same Modern Metals build on the server and client.") + : new TextComponentTranslation("config.modernmetals.connection.protocol"); + } else if (remoteLow == localLow) { + return; + } else { + final boolean serverLow = server ? localLow : remoteLow; + reason = new TextComponentTranslation("config.modernmetals.connection.mismatch", + modeLabel(serverLow), modeLabel(!serverLow)); + } + + final NetworkManager manager = event.getManager(); + manager.channel().config().setAutoRead(false); + if (server) { + manager.sendPacket(new SPacketDisconnect(reason), future -> manager.closeChannel(reason)); + } else { + manager.closeChannel(reason); + } + } + + private static ITextComponent modeLabel(final boolean low) { + return new TextComponentTranslation("config.modernmetals.content_mode." + (low ? "low_fantasy" : "high_fantasy")); + } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/content/ContentPolicy.java b/src/main/java/zone/moddev/mc/modernmetals/content/ContentPolicy.java new file mode 100644 index 00000000..b32c1a18 --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/content/ContentPolicy.java @@ -0,0 +1,79 @@ +package zone.moddev.mc.modernmetals.content; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.common.ModContainer; +import net.minecraftforge.fml.common.versioning.DefaultArtifactVersion; +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.util.MMeConfig; + +/** Restricts obtainability, while retaining every registered item and saved stack. */ +public final class ContentPolicy { + private static final Set TOOLS = names("axe","crackhammer","hoe","pickaxe","scythe","shears","shovel","sword","arrow","bolt"); + private static final Set PROTECTION = names("boots","chestplate","helmet","leggings","horse_armor","horsearmor","shield"); + private static final Set FULL_TOOLS = names("galvanizedsteel","stainlesssteel","titanium"); + private static final Set ARMOR = names("aluminum","aluminumbrass","galvanizedsteel","stainlesssteel","titanium"); + private static final Set GEARS = names("aluminum","aluminumbrass","galvanizedsteel","stainlesssteel","titanium"); + private static final Set STRUCTURAL_FORMS = names("bars","bookshelf","button","door","double_slab","doubleslab","fence","fence_gate","flower_pot","ladder","lever","pressure_plate","pressureplate","slab","stairs","trapdoor","tripwire_hook","wall"); + private static final Set STRUCTURAL_METALS = names("aluminum","aluminumbrass","galvanizedsteel","nichrome","stainlesssteel","tantalum","titanium","tungsten","zirconium"); + private static boolean initialized; + private static ContentPolicy active = forMode("high_fantasy"); + private final boolean low; + private ContentPolicy(final boolean low) { this.low=low; } + public static ContentPolicy forMode(final String mode) { + return new ContentPolicy(mode!=null && "low_fantasy".equals(mode.trim().toLowerCase(Locale.ROOT))); + } + public static ContentPolicy active() { return active; } + public boolean lowFantasy() { return low; } + /** Base Metals owns the mode when its startup configuration API is available. */ + public static boolean inheritsBaseMetals() { + final ModContainer mod = Loader.instance().getIndexedModList().get("basemetals"); + return mod != null && new DefaultArtifactVersion(mod.getVersion()) + .compareTo(new DefaultArtifactVersion("2.6.1.112021")) >= 0; + } + + public static void initialize() { + if (initialized) return; + final boolean inherited = inheritsBaseMetals(); + active = forMode(inherited ? BaseMetalsMode.read() : MMeConfig.configuredContentMode()); + initialized = true; + ModernMetals.logger.info("Modern Metals Fantasy Level %s (%s)", + active.low ? "low_fantasy" : "high_fantasy", inherited ? "Base Metals" : "Modern Metals"); + } + + // This class is linked only in a pack with the compatible Base Metals API. + private static final class BaseMetalsMode { + static String read() { + return com.mcmoddev.basemetals.util.BMeConfig.getActiveContentMode().serializedName(); + } + } + + public boolean allows(final String material,final String form) { + if(!low || material==null || form==null) return true; + if(TOOLS.contains(form)) return FULL_TOOLS.contains(material); + if(PROTECTION.contains(form)) return ARMOR.contains(material); + if ("bow".equals(form) || "crossbow".equals(form) || "fishing_rod".equals(form) + || "fishingrod".equals(form)) return false; + if("gear".equals(form)) return GEARS.contains(material); + if("anvil".equals(form)) return "stainlesssteel".equals(material) || "galvanizedsteel".equals(material); + if(STRUCTURAL_FORMS.contains(form)) return STRUCTURAL_METALS.contains(material); + return true; + } + public boolean allows(final ItemStack stack) { + if(!low || stack==null || stack.isEmpty()) return true; + final ResourceLocation id=stack.getItem().getRegistryName(); + if(id==null || !ModernMetals.MODID.equals(id.getNamespace())) return true; + final String path=id.getPath().startsWith("double_") ? id.getPath().substring(7) : id.getPath(); + final int split=path.indexOf('_'); + return split<0 || allows(path.substring(0,split),path.substring(split+1)); + } + private static Set names(final String... values) { + return Collections.unmodifiableSet(new HashSet<>(Arrays.asList(values))); + } +} diff --git a/src/main/java/com/mcmoddev/modernmetals/data/MaterialNames.java b/src/main/java/zone/moddev/mc/modernmetals/data/MaterialNames.java similarity index 97% rename from src/main/java/com/mcmoddev/modernmetals/data/MaterialNames.java rename to src/main/java/zone/moddev/mc/modernmetals/data/MaterialNames.java index ab7ce948..27083e9e 100644 --- a/src/main/java/com/mcmoddev/modernmetals/data/MaterialNames.java +++ b/src/main/java/zone/moddev/mc/modernmetals/data/MaterialNames.java @@ -1,4 +1,4 @@ -package com.mcmoddev.modernmetals.data; +package zone.moddev.mc.modernmetals.data; import com.mcmoddev.lib.data.SharedStrings; diff --git a/src/main/java/com/mcmoddev/modernmetals/data/TraitNames.java b/src/main/java/zone/moddev/mc/modernmetals/data/TraitNames.java similarity index 94% rename from src/main/java/com/mcmoddev/modernmetals/data/TraitNames.java rename to src/main/java/zone/moddev/mc/modernmetals/data/TraitNames.java index 301f6254..82ededc3 100644 --- a/src/main/java/com/mcmoddev/modernmetals/data/TraitNames.java +++ b/src/main/java/zone/moddev/mc/modernmetals/data/TraitNames.java @@ -1,4 +1,4 @@ -package com.mcmoddev.modernmetals.data; +package zone.moddev.mc.modernmetals.data; import com.mcmoddev.lib.data.SharedStrings; diff --git a/src/main/java/com/mcmoddev/modernmetals/init/Blocks.java b/src/main/java/zone/moddev/mc/modernmetals/init/Blocks.java similarity index 94% rename from src/main/java/com/mcmoddev/modernmetals/init/Blocks.java rename to src/main/java/zone/moddev/mc/modernmetals/init/Blocks.java index 7bae5bd1..370f793f 100644 --- a/src/main/java/com/mcmoddev/modernmetals/init/Blocks.java +++ b/src/main/java/zone/moddev/mc/modernmetals/init/Blocks.java @@ -1,4 +1,4 @@ -package com.mcmoddev.modernmetals.init; +package zone.moddev.mc.modernmetals.init; import java.util.Arrays; import java.util.List; @@ -7,8 +7,8 @@ import com.mcmoddev.lib.data.SharedStrings; import com.mcmoddev.lib.init.Materials; import com.mcmoddev.lib.material.MMDMaterial; -import com.mcmoddev.modernmetals.ModernMetals; -import com.mcmoddev.modernmetals.data.MaterialNames; +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.data.MaterialNames; import net.minecraft.block.Block; import net.minecraftforge.event.RegistryEvent; diff --git a/src/main/java/com/mcmoddev/modernmetals/init/Fluids.java b/src/main/java/zone/moddev/mc/modernmetals/init/Fluids.java similarity index 66% rename from src/main/java/com/mcmoddev/modernmetals/init/Fluids.java rename to src/main/java/zone/moddev/mc/modernmetals/init/Fluids.java index 3cbfe03d..eae747fe 100644 --- a/src/main/java/com/mcmoddev/modernmetals/init/Fluids.java +++ b/src/main/java/zone/moddev/mc/modernmetals/init/Fluids.java @@ -1,14 +1,17 @@ -package com.mcmoddev.modernmetals.init; +package zone.moddev.mc.modernmetals.init; import java.util.Arrays; import java.util.List; import javax.annotation.Nonnull; +import com.mcmoddev.lib.block.InteractiveFluidBlock; import com.mcmoddev.lib.data.SharedStrings; +import com.mcmoddev.lib.material.MMDMaterial; import com.mcmoddev.lib.init.Materials; import com.mcmoddev.lib.util.Config.Options; -import com.mcmoddev.modernmetals.data.MaterialNames; +import zone.moddev.mc.modernmetals.data.MaterialNames; +import net.minecraft.util.DamageSource; /** * This class initializes all fluids in Modern Metals. @@ -34,9 +37,15 @@ public static void init() { materials.stream().filter(Materials::hasMaterial) .filter(Options::isFluidEnabled).forEach(materialName -> { - // if only this was easier... - addFluid(materialName, 2000, 10000, getTemperature(materialName), 10); - addFluidBlock(materialName); + final MMDMaterial material = Materials.getMaterialByName(materialName); + addFluid(material, 2000, 10000, getTemperature(materialName), 10); + material.setFluidBlockGetter(name -> new InteractiveFluidBlock(material.getFluid(), (world, entity) -> { + if (!world.isRemote) { + entity.attackEntityFrom(DamageSource.LAVA, 4.0F); + entity.setFire(4); + } + })); + addFluidBlock(material); }); } diff --git a/src/main/java/com/mcmoddev/modernmetals/init/ItemGroups.java b/src/main/java/zone/moddev/mc/modernmetals/init/ItemGroups.java similarity index 93% rename from src/main/java/com/mcmoddev/modernmetals/init/ItemGroups.java rename to src/main/java/zone/moddev/mc/modernmetals/init/ItemGroups.java index 9ca97f47..573ddbc0 100644 --- a/src/main/java/com/mcmoddev/modernmetals/init/ItemGroups.java +++ b/src/main/java/zone/moddev/mc/modernmetals/init/ItemGroups.java @@ -1,10 +1,10 @@ -package com.mcmoddev.modernmetals.init; +package zone.moddev.mc.modernmetals.init; import com.mcmoddev.lib.data.Names; import com.mcmoddev.lib.data.SharedStrings; import com.mcmoddev.lib.init.Materials; import com.mcmoddev.lib.material.MMDMaterial; -import com.mcmoddev.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.ModernMetals; /** * This class initializes all item groups in Modern Metals. diff --git a/src/main/java/com/mcmoddev/modernmetals/init/Items.java b/src/main/java/zone/moddev/mc/modernmetals/init/Items.java similarity index 97% rename from src/main/java/com/mcmoddev/modernmetals/init/Items.java rename to src/main/java/zone/moddev/mc/modernmetals/init/Items.java index 856a1342..59e5a901 100644 --- a/src/main/java/com/mcmoddev/modernmetals/init/Items.java +++ b/src/main/java/zone/moddev/mc/modernmetals/init/Items.java @@ -1,4 +1,4 @@ -package com.mcmoddev.modernmetals.init; +package zone.moddev.mc.modernmetals.init; import java.util.Arrays; import java.util.List; @@ -8,8 +8,8 @@ import com.mcmoddev.lib.init.Materials; import com.mcmoddev.lib.material.MMDMaterial; import com.mcmoddev.lib.util.Oredicts; -import com.mcmoddev.modernmetals.ModernMetals; -import com.mcmoddev.modernmetals.data.MaterialNames; +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.data.MaterialNames; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; diff --git a/src/main/java/com/mcmoddev/modernmetals/init/Materials.java b/src/main/java/zone/moddev/mc/modernmetals/init/Materials.java similarity index 98% rename from src/main/java/com/mcmoddev/modernmetals/init/Materials.java rename to src/main/java/zone/moddev/mc/modernmetals/init/Materials.java index bf2858ab..88a9c35e 100644 --- a/src/main/java/com/mcmoddev/modernmetals/init/Materials.java +++ b/src/main/java/zone/moddev/mc/modernmetals/init/Materials.java @@ -1,4 +1,4 @@ -package com.mcmoddev.modernmetals.init; +package zone.moddev.mc.modernmetals.init; import java.util.Arrays; import java.util.List; @@ -8,7 +8,7 @@ import com.mcmoddev.lib.data.SharedStrings; import com.mcmoddev.lib.material.MMDMaterialType.MaterialType; import com.mcmoddev.lib.util.Config.Options; -import com.mcmoddev.modernmetals.data.MaterialNames; +import zone.moddev.mc.modernmetals.data.MaterialNames; /** * This class initializes all of the materials in Modern Metals. diff --git a/src/main/java/zone/moddev/mc/modernmetals/init/PolicyRecipe.java b/src/main/java/zone/moddev/mc/modernmetals/init/PolicyRecipe.java new file mode 100644 index 00000000..166e5f70 --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/init/PolicyRecipe.java @@ -0,0 +1,66 @@ +package zone.moddev.mc.modernmetals.init; + +import zone.moddev.mc.modernmetals.content.ContentPolicy; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.util.NonNullList; +import net.minecraft.world.World; +import net.minecraftforge.registries.IForgeRegistryEntry; + +/** Disables restricted recipes without removing their saved registry names. */ +final class PolicyRecipe extends IForgeRegistryEntry.Impl implements IRecipe { + private final IRecipe delegate; + private final ContentPolicy policy; + private final boolean allowed; + + PolicyRecipe(final IRecipe delegate, final ContentPolicy policy) { + this.delegate = delegate; + + this.policy = policy; + this.allowed = policy.allows(delegate.getRecipeOutput()); + setRegistryName(delegate.getRegistryName()); + } + + @Override + public boolean matches(final InventoryCrafting inventory, final World world) { + return allowed && delegate.matches(inventory, world) && policy.allows(delegate.getCraftingResult(inventory)); + } + + @Override + public ItemStack getCraftingResult(final InventoryCrafting inventory) { + if (!allowed) return ItemStack.EMPTY; + final ItemStack result = delegate.getCraftingResult(inventory); + return policy.allows(result) ? result : ItemStack.EMPTY; + } + + @Override + public boolean canFit(final int width, final int height) { + return allowed && delegate.canFit(width, height); + } + + @Override + public ItemStack getRecipeOutput() { + return allowed ? delegate.getRecipeOutput() : ItemStack.EMPTY; + } + + @Override + public NonNullList getRemainingItems(final InventoryCrafting inventory) { + return delegate.getRemainingItems(inventory); + } + + @Override + public NonNullList getIngredients() { + return delegate.getIngredients(); + } + + @Override + public boolean isDynamic() { + return !allowed || delegate.isDynamic(); + } + + @Override + public String getGroup() { + return delegate.getGroup(); + } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/init/Recipes.java b/src/main/java/zone/moddev/mc/modernmetals/init/Recipes.java new file mode 100644 index 00000000..febf6624 --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/init/Recipes.java @@ -0,0 +1,116 @@ +package zone.moddev.mc.modernmetals.init; + +import java.util.ArrayList; +import java.util.List; +import com.mcmoddev.lib.data.Names; +import com.mcmoddev.lib.data.SharedStrings; +import com.mcmoddev.lib.init.Materials; +import com.mcmoddev.lib.material.MMDMaterial; +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.content.ContentPolicy; +import zone.moddev.mc.modernmetals.data.MaterialNames; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.FurnaceRecipes; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.eventhandler.EventPriority; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.registry.GameRegistry; + +/** + * + * @author Jasmine Iwanek + * + */ +@Mod.EventBusSubscriber(modid = ModernMetals.MODID) +public class Recipes extends com.mcmoddev.lib.init.Recipes { + + private Recipes() { + throw new IllegalAccessError(SharedStrings.NOT_INSTANTIABLE); + } + + /** + * + */ + public static void init() { + initModSpecificRecipes(); + } + + @SubscribeEvent(priority = EventPriority.LOWEST) + public static void registerRecipes(final RegistryEvent.Register event) { + initModSpecificRecipes(); + register(event); + registerBlendSmelting(); + final ContentPolicy policy = ContentPolicy.active(); + if (!policy.lowFantasy()) return; + + final List restricted = new ArrayList<>(); + for (final IRecipe recipe : event.getRegistry()) { + final String type = recipe.getClass().getName(); + final boolean sharedDynamic = recipe.getRecipeOutput().isEmpty() + && (type.startsWith("com.mcmoddev.lib.recipe.") || type.startsWith("zone.moddev.mc.mmdlib.recipe.")); + if (!policy.allows(recipe.getRecipeOutput()) || sharedDynamic) { + restricted.add(recipe); + } + } + for (final IRecipe recipe : restricted) { + event.getRegistry().register(new PolicyRecipe(recipe, policy)); + } + } + + private static void registerBlendSmelting() { + for (final MMDMaterial material : Materials.getMaterialsByMod(ModernMetals.MODID)) { + if (!material.hasItem(Names.BLEND) || !material.hasItem(Names.INGOT)) continue; + final ItemStack blend = material.getItemStack(Names.BLEND); + if (FurnaceRecipes.instance().getSmeltingResult(blend).isEmpty()) { + GameRegistry.addSmelting(blend, material.getItemStack(Names.INGOT), material.getOreSmeltXP()); + } + } + } + + private static void initModSpecificRecipes() { + if (Materials.hasMaterial(MaterialNames.ALUMINUM)) { + final MMDMaterial aluminum = Materials.getMaterialByName(MaterialNames.ALUMINUM); + + addAdditionalOredicts(aluminum, "Aluminium"); + addAdditionalOredicts(aluminum, "Bauxite"); + } + + if (Materials.hasMaterial(MaterialNames.ALUMINUM_BRASS)) { + final MMDMaterial aluminumBrass = Materials.getMaterialByName(MaterialNames.ALUMINUM_BRASS); + + addAdditionalOredicts(aluminumBrass, "AluminumBrass"); + addAdditionalOredicts(aluminumBrass, "Aluminiumbrass"); + addAdditionalOredicts(aluminumBrass, "AluminiumBrass"); + addAdditionalOredicts(aluminumBrass, "Alubrass"); + addAdditionalOredicts(aluminumBrass, "AluBrass"); + } + + if (Materials.hasMaterial(MaterialNames.CHROMIUM)) { + addAdditionalOredicts(MaterialNames.CHROMIUM, "Chrome"); + } + + if (Materials.hasMaterial(MaterialNames.GALVANIZED_STEEL)) { + final MMDMaterial galvanizedSteel = Materials.getMaterialByName(MaterialNames.GALVANIZED_STEEL); + + addAdditionalOredicts(galvanizedSteel, "GalvinizedSteel"); + } + + if (Materials.hasMaterial(MaterialNames.NICHROME)) { + final MMDMaterial nichrome = Materials.getMaterialByName(MaterialNames.NICHROME); + + addAdditionalOredicts(nichrome, "Nichrome"); + } + + if (Materials.hasMaterial(MaterialNames.STAINLESS_STEEL)) { + final MMDMaterial stainlessSteel = Materials.getMaterialByName(MaterialNames.STAINLESS_STEEL); + + addAdditionalOredicts(stainlessSteel, "StainlessSteel"); + } + + if (Materials.hasMaterial(MaterialNames.TUNGSTEN)) { + addAdditionalOredicts(MaterialNames.TUNGSTEN, "Wolfram"); + } + } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/init/VillagerTrades.java b/src/main/java/zone/moddev/mc/modernmetals/init/VillagerTrades.java new file mode 100644 index 00000000..ed00507e --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/init/VillagerTrades.java @@ -0,0 +1,46 @@ +package zone.moddev.mc.modernmetals.init; + +import com.mcmoddev.lib.data.MaterialStats; +import com.mcmoddev.lib.data.Names; +import com.mcmoddev.lib.data.SharedStrings; +import com.mcmoddev.lib.init.Materials; +import com.mcmoddev.lib.material.MMDMaterial; +import com.mcmoddev.lib.util.VillagerTradeHelper; +import net.minecraft.item.ItemStack; +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.content.ContentPolicy; + +/** + * + * @author Jasmine Iwanek + * + */ +public class VillagerTrades extends com.mcmoddev.lib.init.VillagerTrades { + private VillagerTrades() { + throw new IllegalAccessError(SharedStrings.NOT_INSTANTIABLE); + } + + /** + * + */ + public static void init() { + if (!ContentPolicy.active().lowFantasy()) { + registerCommonTrades(); + return; + } + // Keep smiths trading metal in Low Fantasy, without offering implausible equipment. + for (final MMDMaterial material : Materials.getMaterialsByMod(ModernMetals.MODID)) { + if (material.isEmpty() || material.isRare() || !material.hasItem(Names.INGOT)) continue; + final float value = material.getStat(MaterialStats.HARDNESS) + + material.getStat(MaterialStats.STRENGTH) + + material.getStat(MaterialStats.MAGICAFFINITY) + material.getToolHarvestLevel(); + final int price = emeraldPurchaseValue(value); + if (price > 64 || emeraldSaleValue(value) > 64) continue; + final ItemStack ingots = material.getItemStack(Names.INGOT, 12); + for (final int career : new int[] { ARMOR_SMITH_ID, WEAPON_SMITH_ID, TOOL_SMITH_ID }) { + VillagerTradeHelper.insertTrades(SMITH_RL, career, tradeLevel(value), + makePurchasePalette(price, ingots)); + } + } + } +} diff --git a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeConstructsArmory.java b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeConstructsArmory.java similarity index 95% rename from src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeConstructsArmory.java rename to src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeConstructsArmory.java index 913ebd67..5cf3f1d3 100644 --- a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeConstructsArmory.java +++ b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeConstructsArmory.java @@ -1,4 +1,4 @@ -package com.mcmoddev.modernmetals.integration.plugins; +package zone.moddev.mc.modernmetals.integration.plugins; import c4.conarm.common.armor.traits.ArmorTraits; import com.mcmoddev.lib.integration.IIntegration; @@ -8,8 +8,8 @@ import com.mcmoddev.lib.integration.plugins.tinkers.events.MaterialRegistrationEvent; import com.mcmoddev.lib.integration.plugins.tinkers.traits.MMDTraits; import com.mcmoddev.lib.util.Config; -import com.mcmoddev.modernmetals.ModernMetals; -import com.mcmoddev.modernmetals.data.MaterialNames; +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.data.MaterialNames; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.common.eventhandler.EventPriority; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; diff --git a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeDenseOres.java b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeDenseOres.java similarity index 90% rename from src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeDenseOres.java rename to src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeDenseOres.java index 4956e573..66f3bedc 100644 --- a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeDenseOres.java +++ b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeDenseOres.java @@ -1,4 +1,4 @@ -package com.mcmoddev.modernmetals.integration.plugins; +package zone.moddev.mc.modernmetals.integration.plugins; import java.util.Arrays; import java.util.List; @@ -9,8 +9,8 @@ import com.mcmoddev.lib.material.MMDMaterial; import com.mcmoddev.lib.util.Config.Options; import com.mcmoddev.lib.util.Oredicts; -import com.mcmoddev.modernmetals.ModernMetals; -import com.mcmoddev.modernmetals.data.MaterialNames; +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.data.MaterialNames; @MMDPlugin(addonId = ModernMetals.MODID, pluginId = MMeDenseOres.PLUGIN_MODID) public class MMeDenseOres extends com.mcmoddev.lib.integration.plugins.DenseOres implements IIntegration { diff --git a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeEnderIO.java b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeEnderIO.java similarity index 88% rename from src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeEnderIO.java rename to src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeEnderIO.java index 429d3b9d..9f3d8891 100644 --- a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeEnderIO.java +++ b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeEnderIO.java @@ -1,4 +1,4 @@ -package com.mcmoddev.modernmetals.integration.plugins; +package zone.moddev.mc.modernmetals.integration.plugins; import java.util.Arrays; import java.util.List; @@ -7,8 +7,8 @@ import com.mcmoddev.lib.integration.IIntegration; import com.mcmoddev.lib.integration.MMDPlugin; import com.mcmoddev.lib.util.Config.Options; -import com.mcmoddev.modernmetals.ModernMetals; -import com.mcmoddev.modernmetals.data.MaterialNames; +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.data.MaterialNames; /** * diff --git a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeIC2.java b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeIC2.java similarity index 95% rename from src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeIC2.java rename to src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeIC2.java index 02c89d99..50b86927 100644 --- a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeIC2.java +++ b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeIC2.java @@ -1,4 +1,4 @@ -package com.mcmoddev.modernmetals.integration.plugins; +package zone.moddev.mc.modernmetals.integration.plugins; import java.util.Arrays; @@ -8,8 +8,8 @@ import com.mcmoddev.lib.integration.MMDPlugin; import com.mcmoddev.lib.material.MMDMaterial; import com.mcmoddev.lib.util.Config.Options; -import com.mcmoddev.modernmetals.ModernMetals; -import com.mcmoddev.modernmetals.data.MaterialNames; +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.data.MaterialNames; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.RegistryEvent; diff --git a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeMekanism.java b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeMekanism.java similarity index 93% rename from src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeMekanism.java rename to src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeMekanism.java index 4577451c..420c2cc8 100644 --- a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeMekanism.java +++ b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeMekanism.java @@ -1,4 +1,4 @@ -package com.mcmoddev.modernmetals.integration.plugins; +package zone.moddev.mc.modernmetals.integration.plugins; import java.util.Arrays; import java.util.List; @@ -8,8 +8,8 @@ import com.mcmoddev.lib.integration.MMDPlugin; import com.mcmoddev.lib.integration.plugins.Mekanism; import com.mcmoddev.lib.util.Config.Options; -import com.mcmoddev.modernmetals.ModernMetals; -import com.mcmoddev.modernmetals.data.MaterialNames; +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.data.MaterialNames; import net.minecraft.item.crafting.IRecipe; import net.minecraftforge.common.MinecraftForge; diff --git a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeThaumcraft.java b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeThaumcraft.java similarity index 80% rename from src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeThaumcraft.java rename to src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeThaumcraft.java index be4c5dbe..c669e075 100644 --- a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeThaumcraft.java +++ b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeThaumcraft.java @@ -1,9 +1,9 @@ -package com.mcmoddev.modernmetals.integration.plugins; +package zone.moddev.mc.modernmetals.integration.plugins; import com.mcmoddev.lib.integration.IIntegration; import com.mcmoddev.lib.integration.MMDPlugin; import com.mcmoddev.lib.util.Config.Options; -import com.mcmoddev.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.ModernMetals; @MMDPlugin(addonId = ModernMetals.MODID, pluginId = MMeThaumcraft.PLUGIN_MODID) public class MMeThaumcraft extends com.mcmoddev.lib.integration.plugins.Thaumcraft implements IIntegration { diff --git a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeThermalExpansion.java b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeThermalExpansion.java similarity index 79% rename from src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeThermalExpansion.java rename to src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeThermalExpansion.java index ffc0a422..3e4ca4ae 100644 --- a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeThermalExpansion.java +++ b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeThermalExpansion.java @@ -1,4 +1,4 @@ -package com.mcmoddev.modernmetals.integration.plugins; +package zone.moddev.mc.modernmetals.integration.plugins; import java.util.Arrays; import java.util.List; @@ -9,8 +9,8 @@ import com.mcmoddev.lib.integration.MMDPlugin; import com.mcmoddev.lib.material.MMDMaterial; import com.mcmoddev.lib.util.Config.Options; -import com.mcmoddev.modernmetals.ModernMetals; -import com.mcmoddev.modernmetals.data.MaterialNames; +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.data.MaterialNames; import cofh.api.util.ThermalExpansionHelper; import net.minecraft.item.crafting.IRecipe; @@ -55,21 +55,21 @@ public void regShit(final RegistryEvent.Register event) { maybeAddMagmaticFuels(MaterialNames.PLUTONIUM, MaterialNames.URANIUM); - if ((hasMaterials(com.mcmoddev.modernmetals.data.MaterialNames.STEEL, MaterialNames.CHROMIUM, MaterialNames.STAINLESS_STEEL)) - && (materialsHaveItems(Arrays.asList(com.mcmoddev.modernmetals.data.MaterialNames.STEEL, MaterialNames.CHROMIUM, MaterialNames.STAINLESS_STEEL), Names.INGOT.toString()))) { - final MMDMaterial steel = Materials.getMaterialByName(com.mcmoddev.modernmetals.data.MaterialNames.STEEL); + if ((hasMaterials(zone.moddev.mc.modernmetals.data.MaterialNames.STEEL, MaterialNames.CHROMIUM, MaterialNames.STAINLESS_STEEL)) + && (materialsHaveItems(Arrays.asList(zone.moddev.mc.modernmetals.data.MaterialNames.STEEL, MaterialNames.CHROMIUM, MaterialNames.STAINLESS_STEEL), Names.INGOT.toString()))) { + final MMDMaterial steel = Materials.getMaterialByName(zone.moddev.mc.modernmetals.data.MaterialNames.STEEL); final MMDMaterial chromium = Materials.getMaterialByName(MaterialNames.CHROMIUM); final MMDMaterial stainlessSteel = Materials.getMaterialByName(MaterialNames.STAINLESS_STEEL); ThermalExpansionHelper.addSmelterRecipe(4000, steel.getItemStack(Names.INGOT, 1), chromium.getItemStack(Names.INGOT, 1), stainlessSteel.getItemStack(Names.INGOT, 2)); } - if ((hasMaterials(com.mcmoddev.modernmetals.data.MaterialNames.STEEL, com.mcmoddev.modernmetals.data.MaterialNames.ZINC, MaterialNames.GALVANIZED_STEEL)) - && (materialsHaveItems(Arrays.asList(com.mcmoddev.modernmetals.data.MaterialNames.STEEL, com.mcmoddev.modernmetals.data.MaterialNames.ZINC, MaterialNames.GALVANIZED_STEEL), Names.INGOT.toString()))) { - final MMDMaterial steel = Materials.getMaterialByName(com.mcmoddev.modernmetals.data.MaterialNames.STEEL); + if ((hasMaterials(zone.moddev.mc.modernmetals.data.MaterialNames.STEEL, zone.moddev.mc.modernmetals.data.MaterialNames.ZINC, MaterialNames.GALVANIZED_STEEL)) + && (materialsHaveItems(Arrays.asList(zone.moddev.mc.modernmetals.data.MaterialNames.STEEL, zone.moddev.mc.modernmetals.data.MaterialNames.ZINC, MaterialNames.GALVANIZED_STEEL), Names.INGOT.toString()))) { + final MMDMaterial steel = Materials.getMaterialByName(zone.moddev.mc.modernmetals.data.MaterialNames.STEEL); final MMDMaterial galvanizedSteel = Materials.getMaterialByName(MaterialNames.GALVANIZED_STEEL); final MMDMaterial zinc = Materials - .getMaterialByName(com.mcmoddev.modernmetals.data.MaterialNames.ZINC); + .getMaterialByName(zone.moddev.mc.modernmetals.data.MaterialNames.ZINC); ThermalExpansionHelper.addSmelterRecipe(4000, steel.getItemStack(Names.INGOT, 1), zinc.getItemStack(Names.INGOT, 1), galvanizedSteel.getItemStack(Names.INGOT, 2)); } diff --git a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeTinkersConstruct.java b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeTinkersConstruct.java similarity index 75% rename from src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeTinkersConstruct.java rename to src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeTinkersConstruct.java index 73f4e902..7001fdc4 100644 --- a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeTinkersConstruct.java +++ b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeTinkersConstruct.java @@ -1,8 +1,5 @@ -package com.mcmoddev.modernmetals.integration.plugins; +package zone.moddev.mc.modernmetals.integration.plugins; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; import java.util.Locale; import com.mcmoddev.lib.init.Materials; @@ -15,9 +12,10 @@ import com.mcmoddev.lib.integration.plugins.tinkers.events.TinkersAlloyRecipeEvent; import com.mcmoddev.lib.util.Config.Options; -import com.mcmoddev.modernmetals.ModernMetals; -import com.mcmoddev.modernmetals.data.MaterialNames; -import com.mcmoddev.modernmetals.data.TraitNames; +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.content.ContentPolicy; +import zone.moddev.mc.modernmetals.data.MaterialNames; +import zone.moddev.mc.modernmetals.data.TraitNames; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fluids.FluidRegistry; @@ -25,6 +23,11 @@ import net.minecraftforge.fml.common.Loader; import net.minecraftforge.fml.common.ModContainer; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import slimeknights.tconstruct.library.events.TinkerRegisterEvent; +import slimeknights.tconstruct.library.smeltery.CastingRecipe; +import slimeknights.tconstruct.library.smeltery.ICastingRecipe; +import slimeknights.tconstruct.library.tools.IToolPart; +import net.minecraft.item.ItemStack; /** * @@ -119,7 +122,7 @@ public void materialRegistration(MaterialRegistrationEvent ev) { private void registerMaterial(final boolean active, final String name, final boolean castable, final boolean craftable, MaterialRegistrationEvent ev, final Object... traits) { - if (active) { + if (active && Materials.getMaterialByName(name).getFluid() != null) { TinkersMaterial mat = new TinkersMaterial(Materials.getMaterialByName(name)) .setCastable(castable).setCraftable(craftable).setToolForge(true); int i = 0; @@ -145,38 +148,46 @@ private void registerMaterial(final boolean active, final String name, final boo private void registerMaterial(final boolean active, final String name, final boolean castable, final boolean craftable, MaterialRegistrationEvent ev) { - if (active) { - TinkersMaterial mat = new TinkersMaterial(Materials.getMaterialByName(name)) - .setCastable(castable).setCraftable(craftable).setToolForge(true).create(); - - ev.getRegistry().register(mat); - } + registerMaterial(active, name, castable, craftable, ev, new Object[0]); } private void registerAlloy(TinkersAlloyRecipeEvent ev, final String outputMaterialName, final int outputAmount, final Object[] recipe) { - final List materialNames = new ArrayList<>(); - materialNames.add(outputMaterialName); - Arrays.asList(recipe).stream() - .filter(elem -> elem instanceof String) - .forEach(elem -> materialNames.add((String)elem)); - if (!hasMaterials(materialNames)) { - return; - } - + if (FluidRegistry.getFluid(outputMaterialName) == null) return; final FluidStack[] rest = new FluidStack[recipe.length / 2]; for (int i = 0, j = 0; i < recipe.length; i += 2, j++) { rest[j] = FluidRegistry.getFluidStack((String)recipe[i], (int)recipe[i + 1]); + if (rest[j] == null) return; } ev.addAlloyRecipe(outputMaterialName, outputAmount, rest); } - private boolean hasMaterials(final List materialNames) { - for (final String name : materialNames) { - if (!Materials.hasMaterial(name)) { - return false; - } + @SubscribeEvent + public void tableCasting(final TinkerRegisterEvent.TableCastingRegisterEvent event) { + restrictCasting(event); + } + + @SubscribeEvent + public void basinCasting(final TinkerRegisterEvent.BasinCastingRegisterEvent event) { + restrictCasting(event); + } + + private static void restrictCasting(final TinkerRegisterEvent event) { + final ContentPolicy policy = ContentPolicy.active(); + if (!policy.lowFantasy() || !(event.getRecipe() instanceof CastingRecipe)) return; + final ItemStack output = ((CastingRecipe) event.getRecipe()).getResult(); + if (!policy.allows(output)) { + event.setCanceled(true); + return; + } + if (output.isEmpty() || !(output.getItem() instanceof IToolPart)) return; + final IToolPart part = (IToolPart) output.getItem(); + final String name = part.getMaterialID(output); + final boolean modernMaterial = Materials.getMaterialsByMod(ModernMetals.MODID).stream() + .anyMatch(material -> name.equals(material.getName())); + if (modernMaterial && (!policy.allows(name, "pickaxe") + || part.hasUseForStat("bow") || part.hasUseForStat("bowstring"))) { + event.setCanceled(true); } - return true; } @SubscribeEvent @@ -184,9 +195,9 @@ public void registerMyAlloys(TinkersAlloyRecipeEvent ev) { ModContainer oldActive = Loader.instance().activeModContainer(); ModContainer newActive = Loader.instance().getIndexedModList().get(ModernMetals.MODID); Loader.instance().setActiveModContainer(newActive); - registerAlloy(ev, MaterialNames.GALVANIZED_STEEL, 2, new Object[] { com.mcmoddev.modernmetals.data.MaterialNames.STEEL, 1, com.mcmoddev.modernmetals.data.MaterialNames.ZINC, 1 }); - registerAlloy(ev, MaterialNames.NICHROME, 2, new Object[] { com.mcmoddev.modernmetals.data.MaterialNames.NICKEL, 1, MaterialNames.CHROMIUM, 1 }); - registerAlloy(ev, MaterialNames.STAINLESS_STEEL, 2, new Object[] { com.mcmoddev.modernmetals.data.MaterialNames.STEEL, 1, MaterialNames.CHROMIUM, 1 }); + registerAlloy(ev, MaterialNames.GALVANIZED_STEEL, 2, new Object[] { zone.moddev.mc.modernmetals.data.MaterialNames.STEEL, 1, zone.moddev.mc.modernmetals.data.MaterialNames.ZINC, 1 }); + registerAlloy(ev, MaterialNames.NICHROME, 2, new Object[] { zone.moddev.mc.modernmetals.data.MaterialNames.NICKEL, 1, MaterialNames.CHROMIUM, 1 }); + registerAlloy(ev, MaterialNames.STAINLESS_STEEL, 2, new Object[] { zone.moddev.mc.modernmetals.data.MaterialNames.STEEL, 1, MaterialNames.CHROMIUM, 1 }); registerAlloy(ev, MaterialNames.TITANIUM, 2, new Object[] { MaterialNames.RUTILE, 1, MaterialNames.MAGNESIUM, 1 }); Loader.instance().setActiveModContainer(oldActive); } diff --git a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeVeinMiner.java b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeVeinMiner.java similarity index 89% rename from src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeVeinMiner.java rename to src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeVeinMiner.java index 9001c153..1f620bdd 100644 --- a/src/main/java/com/mcmoddev/modernmetals/integration/plugins/MMeVeinMiner.java +++ b/src/main/java/zone/moddev/mc/modernmetals/integration/plugins/MMeVeinMiner.java @@ -1,4 +1,4 @@ -package com.mcmoddev.modernmetals.integration.plugins; +package zone.moddev.mc.modernmetals.integration.plugins; import java.util.Arrays; import java.util.List; @@ -8,8 +8,8 @@ import com.mcmoddev.lib.integration.MMDPlugin; import com.mcmoddev.lib.integration.plugins.VeinMiner; import com.mcmoddev.lib.util.Config.Options; -import com.mcmoddev.modernmetals.ModernMetals; -import com.mcmoddev.modernmetals.data.MaterialNames; +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.data.MaterialNames; /** * VeinMiner Integration Plugin. diff --git a/src/main/java/com/mcmoddev/modernmetals/proxy/ClientProxy.java b/src/main/java/zone/moddev/mc/modernmetals/proxy/ClientProxy.java similarity index 66% rename from src/main/java/com/mcmoddev/modernmetals/proxy/ClientProxy.java rename to src/main/java/zone/moddev/mc/modernmetals/proxy/ClientProxy.java index cd4468ae..5416d17b 100644 --- a/src/main/java/com/mcmoddev/modernmetals/proxy/ClientProxy.java +++ b/src/main/java/zone/moddev/mc/modernmetals/proxy/ClientProxy.java @@ -1,12 +1,16 @@ -package com.mcmoddev.modernmetals.proxy; +package zone.moddev.mc.modernmetals.proxy; import com.mcmoddev.lib.client.registrations.RegistrationHelper; import com.mcmoddev.lib.material.MMDMaterial; -import com.mcmoddev.modernmetals.init.Blocks; -import com.mcmoddev.modernmetals.init.Fluids; -import com.mcmoddev.modernmetals.init.Items; +import zone.moddev.mc.modernmetals.init.Blocks; +import zone.moddev.mc.modernmetals.init.Fluids; +import zone.moddev.mc.modernmetals.init.Items; import net.minecraftforge.client.event.ModelRegistryEvent; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.client.ModernMetalsConfigScreen; +import zone.moddev.mc.orespawn.api.client.WorldSettingsExtensionRegistry; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; @@ -27,6 +31,12 @@ public void preInit(final FMLPreInitializationEvent event) { MinecraftForge.EVENT_BUS.register(this); } + @Override + public void init(final FMLInitializationEvent event) { + super.init(event); + WorldSettingsExtensionRegistry.registerConfigScreen(ModernMetals.MODID, ModernMetalsConfigScreen::new); + } + /** * * @param event The Event. diff --git a/src/main/java/zone/moddev/mc/modernmetals/proxy/CommonProxy.java b/src/main/java/zone/moddev/mc/modernmetals/proxy/CommonProxy.java new file mode 100644 index 00000000..6504d0b4 --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/proxy/CommonProxy.java @@ -0,0 +1,50 @@ +package zone.moddev.mc.modernmetals.proxy; + + +import zone.moddev.mc.modernmetals.content.ContentModeNetwork; +import zone.moddev.mc.modernmetals.data.MaterialNames; +import zone.moddev.mc.modernmetals.init.ItemGroups; +import zone.moddev.mc.modernmetals.init.VillagerTrades; +import zone.moddev.mc.modernmetals.util.MMeConfig; + +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; + +/** + * Modern Metals Common Proxy. + * + * @author Jasmine Iwanek + * + */ +public class CommonProxy { + + /** + * + * @param event The Event. + */ + @SuppressWarnings("deprecation") + public void preInit(final FMLPreInitializationEvent event) { + + ContentModeNetwork.register(); + if (MMeConfig.villagerTradesEnabled()) { + VillagerTrades.init(); + } + } + + /** + * + * @param event The Event. + */ + public void init(final FMLInitializationEvent event) { + ItemGroups.setupIcons(MaterialNames.ALUMINUM); + } + + /** + * + * @param event The Event. + */ + public void postInit(FMLPostInitializationEvent event) { + MMeConfig.postInit(); + } +} diff --git a/src/main/java/com/mcmoddev/modernmetals/proxy/ServerProxy.java b/src/main/java/zone/moddev/mc/modernmetals/proxy/ServerProxy.java similarity index 77% rename from src/main/java/com/mcmoddev/modernmetals/proxy/ServerProxy.java rename to src/main/java/zone/moddev/mc/modernmetals/proxy/ServerProxy.java index a8052827..1e68b620 100644 --- a/src/main/java/com/mcmoddev/modernmetals/proxy/ServerProxy.java +++ b/src/main/java/zone/moddev/mc/modernmetals/proxy/ServerProxy.java @@ -1,4 +1,4 @@ -package com.mcmoddev.modernmetals.proxy; +package zone.moddev.mc.modernmetals.proxy; /** * Modern Metals Server Proxy. diff --git a/src/main/java/com/mcmoddev/modernmetals/util/EventHandler.java b/src/main/java/zone/moddev/mc/modernmetals/util/EventHandler.java similarity index 82% rename from src/main/java/com/mcmoddev/modernmetals/util/EventHandler.java rename to src/main/java/zone/moddev/mc/modernmetals/util/EventHandler.java index c7b7bc39..b9738deb 100644 --- a/src/main/java/com/mcmoddev/modernmetals/util/EventHandler.java +++ b/src/main/java/zone/moddev/mc/modernmetals/util/EventHandler.java @@ -1,69 +1,69 @@ -package com.mcmoddev.modernmetals.util; - -import com.mcmoddev.modernmetals.init.Blocks; -import com.mcmoddev.modernmetals.init.Fluids; -import com.mcmoddev.modernmetals.init.Items; -import com.mcmoddev.modernmetals.init.Materials; -import com.mcmoddev.lib.events.MMDLibRegisterBlocks; -import com.mcmoddev.lib.events.MMDLibRegisterFluids; -import com.mcmoddev.lib.events.MMDLibRegisterItems; -import com.mcmoddev.lib.events.MMDLibRegisterMaterials; - -import com.mcmoddev.modernmetals.ModernMetals; - -import net.minecraft.block.Block; -import net.minecraft.item.Item; -import net.minecraftforge.event.RegistryEvent; -import net.minecraftforge.fml.common.Mod.EventBusSubscriber; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; - -@EventBusSubscriber(modid=ModernMetals.MODID) -public class EventHandler { - - @SubscribeEvent - public static void mmdlibRegisterMaterials(final MMDLibRegisterMaterials event) { - Materials.init(); - } - - @SubscribeEvent - public static void mmdlibRegisterBlocks(final MMDLibRegisterBlocks event) { - Blocks.init(); - } - - @SubscribeEvent - public static void mmdlibRegisterItems(final MMDLibRegisterItems event) { - Items.init(); - } - - @SubscribeEvent - public static void mmdlibRegisterFluids(final MMDLibRegisterFluids event) { - Fluids.init(); - } - - /** - * - * @param event The Event. - */ - @SubscribeEvent - public static void onRemapBlock(final RegistryEvent.MissingMappings event) { - for (final RegistryEvent.MissingMappings.Mapping mapping : event.getAllMappings()) { - if (mapping.key.getNamespace().equals(ModernMetals.MODID)) { - // dummy - } - } - } - - /** - * - * @param event The Event. - */ - @SubscribeEvent - public static void onRemapItem(final RegistryEvent.MissingMappings event) { - for (final RegistryEvent.MissingMappings.Mapping mapping : event.getAllMappings()) { - if (mapping.key.getNamespace().equals(ModernMetals.MODID)) { - // dummy - } - } - } - -} +package zone.moddev.mc.modernmetals.util; + +import zone.moddev.mc.modernmetals.init.Blocks; +import zone.moddev.mc.modernmetals.init.Fluids; +import zone.moddev.mc.modernmetals.init.Items; +import zone.moddev.mc.modernmetals.init.Materials; +import com.mcmoddev.lib.events.MMDLibRegisterBlocks; +import com.mcmoddev.lib.events.MMDLibRegisterFluids; +import com.mcmoddev.lib.events.MMDLibRegisterItems; +import com.mcmoddev.lib.events.MMDLibRegisterMaterials; + +import zone.moddev.mc.modernmetals.ModernMetals; + +import net.minecraft.block.Block; +import net.minecraft.item.Item; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; + +@EventBusSubscriber(modid=ModernMetals.MODID) +public class EventHandler { + + @SubscribeEvent + public static void mmdlibRegisterMaterials(final MMDLibRegisterMaterials event) { + Materials.init(); + } + + @SubscribeEvent + public static void mmdlibRegisterBlocks(final MMDLibRegisterBlocks event) { + Blocks.init(); + } + + @SubscribeEvent + public static void mmdlibRegisterItems(final MMDLibRegisterItems event) { + Items.init(); + } + + @SubscribeEvent + public static void mmdlibRegisterFluids(final MMDLibRegisterFluids event) { + Fluids.init(); + } + + /** + * + * @param event The Event. + */ + @SubscribeEvent + public static void onRemapBlock(final RegistryEvent.MissingMappings event) { + for (final RegistryEvent.MissingMappings.Mapping mapping : event.getAllMappings()) { + if (mapping.key.getNamespace().equals(ModernMetals.MODID)) { + // dummy + } + } + } + + /** + * + * @param event The Event. + */ + @SubscribeEvent + public static void onRemapItem(final RegistryEvent.MissingMappings event) { + for (final RegistryEvent.MissingMappings.Mapping mapping : event.getAllMappings()) { + if (mapping.key.getNamespace().equals(ModernMetals.MODID)) { + // dummy + } + } + } + +} diff --git a/src/main/java/com/mcmoddev/modernmetals/util/MMeConfig.java b/src/main/java/zone/moddev/mc/modernmetals/util/MMeConfig.java similarity index 54% rename from src/main/java/com/mcmoddev/modernmetals/util/MMeConfig.java rename to src/main/java/zone/moddev/mc/modernmetals/util/MMeConfig.java index 6a5c5680..925bbcc8 100644 --- a/src/main/java/com/mcmoddev/modernmetals/util/MMeConfig.java +++ b/src/main/java/zone/moddev/mc/modernmetals/util/MMeConfig.java @@ -1,15 +1,20 @@ -package com.mcmoddev.modernmetals.util; +package zone.moddev.mc.modernmetals.util; import com.mcmoddev.lib.registry.CrusherRecipeRegistry; import com.mcmoddev.lib.util.MaterialConfigOptions; -import com.mcmoddev.modernmetals.ModernMetals; -import com.mcmoddev.modernmetals.data.MaterialNames; +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.data.MaterialNames; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.config.Configuration; import net.minecraftforge.fml.client.event.ConfigChangedEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import java.io.File; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import net.minecraftforge.common.config.Property; +import org.apache.commons.lang3.text.WordUtils; /** * @author Jasmine Iwanek @@ -17,7 +22,9 @@ */ public class MMeConfig extends com.mcmoddev.lib.util.Config { - private static Configuration configuration; + public static final List MATERIAL_CATEGORIES = Collections.unmodifiableList( + Arrays.asList("Metals", "Fluids", "Forced Traits")); + private static Configuration configuration; private static final String CONFIG_FILE = "config/ModernMetals.cfg"; private static final MaterialConfigOptions[] MATERIAL_CONFIG_OPTIONS = new MaterialConfigOptions[]{ @@ -63,13 +70,50 @@ public static void init() { MinecraftForge.EVENT_BUS.register(new MMeConfig()); } - configMaterialOptions(MATERIAL_CONFIG_OPTIONS, configuration); + final Property mode = configuration.get("General", "contentMode", "high_fantasy", + "Applies when a compatible Base Metals build is absent. Restart after changing.", + new String[] { "high_fantasy", "low_fantasy" }); + mode.setLanguageKey("config.modernmetals.content_mode").setRequiresMcRestart(true); + mode.setValidValuesDisplay(new String[] { + "config.modernmetals.content_mode.high_fantasy", "config.modernmetals.content_mode.low_fantasy" }); + configuration.get("General", "enableVillagerTrades", true) + .setLanguageKey("config.modernmetals.villager_trades").setRequiresMcRestart(true); + configMaterialOptions(MATERIAL_CONFIG_OPTIONS, configuration); + for (final String category : MATERIAL_CATEGORIES) { + configuration.getCategory(category).setRequiresMcRestart(true); + } + for (final MaterialConfigOptions material : MATERIAL_CONFIG_OPTIONS) { + final String name = material.getIdentifier(); + final String suffix = WordUtils.capitalizeFully(name); + decorate("Metals", "Enable" + suffix, name); + decorate("Fluids", "Enabled " + suffix, name); + decorate("Forced Traits", "Force" + suffix + "TraitRegistration", name); + } if (configuration.hasChanged()) { configuration.save(); } } + private static void decorate(final String category, final String key, final String material) { + final Property property = configuration.getCategory(category).get(key); + if (property != null) { + property.setLanguageKey("config.modernmetals.enable_" + material).setRequiresMcRestart(true); + } + } + + public static Configuration getConfiguration() { + return configuration; + } + + public static String configuredContentMode() { + return configuration.getCategory("General").get("contentMode").getString(); + } + + public static boolean villagerTradesEnabled() { + return configuration.getCategory("General").get("enableVillagerTrades").getBoolean(); + } + public static void postInit() { CrusherRecipeRegistry.getInstance().clearCache(); } diff --git a/src/main/java/zone/moddev/mc/modernmetals/worldgen/ModernMetalsWorldgenProvider.java b/src/main/java/zone/moddev/mc/modernmetals/worldgen/ModernMetalsWorldgenProvider.java new file mode 100644 index 00000000..90260f0a --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/worldgen/ModernMetalsWorldgenProvider.java @@ -0,0 +1,85 @@ +package zone.moddev.mc.modernmetals.worldgen; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.common.registry.ForgeRegistries; +import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.orespawn.api.GeologyFamily; +import zone.moddev.mc.orespawn.api.OreDimensionSelector; +import zone.moddev.mc.orespawn.api.OrePattern; +import zone.moddev.mc.orespawn.api.OreSpawnApi; +import zone.moddev.mc.orespawn.api.WorldgenProvider; + +/** Native declarations only; OreSpawn resolves, bakes and executes generation. */ +public final class ModernMetalsWorldgenProvider { + private static final Ore[] ORES = { + new Ore("boron", 0, 31, 8D, 8, 8, true), + new Ore("thorium", 0, 31, 8D, 8, 4, true), + new Ore("beryllium", 0, 127, 20D, 5, 3, true), + new Ore("aluminum", 0, 95, 8D, 10, 8, true), + new Ore("cadmium", 0, 95, 8D, 4, 8, true), + new Ore("chromium", 0, 31, 3D, 2, 4, true), + new Ore("iridium", 0, 63, 5D, 6, 4, true), + new Ore("magnesium", 0, 95, 8D, 6, 4, true), + new Ore("manganese", 0, 63, 6D, 6, 4, true), + new Ore("osmium", 0, 95, 8D, 10, 4, true), + new Ore("plutonium", 0, 31, 4D, 2, 4, true), + new Ore("rutile", 0, 63, 6D, 6, 4, true), + new Ore("tantalum", 0, 63, 6D, 6, 4, true), + new Ore("tungsten", 0, 31, 6D, 4, 4, true), + new Ore("uranium", 0, 31, 6D, 2, 4, true), + new Ore("zirconium", 0, 63, 6D, 8, 4, true), + new Ore("titanium", 0, 63, 6D, 6, 4, false) + }; + private ModernMetalsWorldgenProvider() { } + public static void enqueue() { + build(ForgeRegistries.BLOCKS::containsKey).ifPresent(provider -> { + if (!OreSpawnApi.enqueue(provider)) throw new IllegalStateException("Modern Metals provider registered twice"); + }); + for (final Ore ore : ORES) { + final int minimum = ore.size - ore.variation; + if (minimum < 1 && ForgeRegistries.BLOCKS.containsKey(id("modernmetals", ore.name + "_ore"))) { + ModernMetals.logger.info("OreSpawn native quantity minimum for " + ore.name + + " is clamped from " + minimum + " to 1, matching the legacy importer"); + } + } + } + public static Optional build(final Predicate available) { + final List ores = new ArrayList<>(); + for (final Ore rule : ORES) { + final ResourceLocation block = id("modernmetals", rule.name + "_ore"); + if (!available.test(block)) continue; + final WorldgenProvider.OreDefinition.Builder ore = WorldgenProvider.OreDefinition + .builder(id("modernmetals", "legacy/" + rule.name + "_ore"), block) + .enabled(rule.enabled).nativeGeneration(false).retrogen(false) + .material(id("orespawn", rule.name)).output(block, 100D); + ore.dimensionSelector(OreDimensionSelector.ALL_EXCEPT_NETHER_AND_END, placement -> { + placement.yRange(rule.minY, rule.maxY).attempts(rule.frequency) + .quantityRange(Math.max(1, rule.size - rule.variation), Math.min(64, rule.size + rule.variation - 1)) + .pattern(OrePattern.DEFAULT).placementChannel(id("orespawn", "standard")) + .hostBlock(id("minecraft", "stone")).hostBlock(id("minecraft", "netherrack")) + .hostBlock(id("minecraft", "end_stone")); + for (final GeologyFamily family : GeologyFamily.values()) placement.hostFamily(family); + }); + ores.add(ore.build()); + } + if (ores.isEmpty()) return Optional.empty(); + final WorldgenProvider.Builder provider = WorldgenProvider.builder("modernmetals", 1); + ores.forEach(provider::ore); + return Optional.of(provider.build()); + } + private static ResourceLocation id(final String namespace, final String path) { return new ResourceLocation(namespace, path); } + private static final class Ore { + final String name; + final int minY, maxY, size, variation; + final double frequency; + final boolean enabled; + Ore(String name, int minY, int maxY, double frequency, int size, int variation, boolean enabled) { + this.name = name; this.minY = minY; this.maxY = maxY; this.frequency = frequency; + this.size = size; this.variation = variation; this.enabled = enabled; + } + } +} diff --git a/src/main/resources/META-INF/modernmetals-legacy-api.list b/src/main/resources/META-INF/modernmetals-legacy-api.list new file mode 100644 index 00000000..e7804639 --- /dev/null +++ b/src/main/resources/META-INF/modernmetals-legacy-api.list @@ -0,0 +1,24 @@ +data/MaterialNames +data/TraitNames +init/Blocks +init/Fluids +init/ItemGroups +init/Items +init/Materials +init/Recipes +init/VillagerTrades +integration/plugins/MMeConstructsArmory +integration/plugins/MMeDenseOres +integration/plugins/MMeEnderIO +integration/plugins/MMeIC2 +integration/plugins/MMeMekanism +integration/plugins/MMeThaumcraft +integration/plugins/MMeThermalExpansion +integration/plugins/MMeTinkersConstruct +integration/plugins/MMeVeinMiner +ModernMetals +proxy/ClientProxy +proxy/CommonProxy +proxy/ServerProxy +util/EventHandler +util/MMeConfig diff --git a/src/main/resources/assets/modernmetals/lang/de_DE.lang b/src/main/resources/assets/modernmetals/lang/de_DE.lang index 815157a6..1544ff4d 100644 --- a/src/main/resources/assets/modernmetals/lang/de_DE.lang +++ b/src/main/resources/assets/modernmetals/lang/de_DE.lang @@ -1313,4 +1313,40 @@ tile.modernmetals.zirconium_lever.name=Zirkonhebel tile.modernmetals.zirconium_pressure_plate.name=Zirkondruckplatte tile.modernmetals.zirconium_slab.name=Zirkonfliese tile.modernmetals.zirconium_stairs.name=Zirkontreppe -tile.modernmetals.zirconium_wall.name=Zirkonmauer \ No newline at end of file +tile.modernmetals.zirconium_wall.name=Zirkonmauer + +config.modernmetals.material.tooltip=Legt fest, ob die Blöcke, Gegenstände und Rezepte dieses Materials verfügbar sind. Ein Neustart ist nötig. +config.modernmetals.title=Modern Metals +config.modernmetals.description=Neustart erforderlich. Erze in OreSpawn einstellen. +config.modernmetals.content_mode=Fantasy-Stufe +config.modernmetals.content_mode.tooltip=Viel Fantasy behält alle Rezepte bei. Wenig Fantasy schränkt unrealistische Ausrüstung und Bauteile ein. Ein Neustart ist nötig. +high_fantasy=Viel Fantasy +low_fantasy=Wenig Fantasy +config.modernmetals.villager_trades=Dorfbewohnerhandel +config.modernmetals.villager_trades.tooltip=Schmiede handeln mit Barren und Ausrüstung aus Modern Metals. Ein Neustart ist nötig. +config.modernmetals.connection.mismatch=Der Server verwendet %s, dein Spiel verwendet %s. Gleiche die Fantasy-Stufe unter Mods > Modern Metals > Konfiguration an und starte das Spiel neu. +config.modernmetals.connection.protocol=Modern Metals konnte die Fantasy-Stufe nicht prüfen. Verwende auf dem Server und im Spiel denselben Build von Modern Metals. +config.modernmetals.enable_thorium=Thorium aktivieren +config.modernmetals.enable_boron=Bor aktivieren +config.modernmetals.enable_beryllium=Beryllium aktivieren +config.modernmetals.enable_cadmium=Cadmium aktivieren +config.modernmetals.enable_nichrome=Nickel-Chrom-Legierung aktivieren +config.modernmetals.enable_aluminum=Aluminium aktivieren +config.modernmetals.enable_aluminumbrass=Aluminiummessing aktivieren +config.modernmetals.enable_chromium=Chrom aktivieren +config.modernmetals.enable_galvanizedsteel=Verzinkter Stahl aktivieren +config.modernmetals.enable_iridium=Iridium aktivieren +config.modernmetals.enable_magnesium=Magnesium aktivieren +config.modernmetals.enable_manganese=Mangan aktivieren +config.modernmetals.enable_osmium=Osmium aktivieren +config.modernmetals.enable_plutonium=Plutonium aktivieren +config.modernmetals.enable_rutile=Rutil aktivieren +config.modernmetals.enable_stainlesssteel=Edelstahl aktivieren +config.modernmetals.enable_tantalum=Tantal aktivieren +config.modernmetals.enable_titanium=Titan aktivieren +config.modernmetals.enable_tungsten=Wolfram aktivieren +config.modernmetals.enable_uranium=Uran aktivieren +config.modernmetals.enable_zirconium=Zirkon aktivieren +config.modernmetals.content_mode.high_fantasy=Viel Fantasy +config.modernmetals.content_mode.low_fantasy=Wenig Fantasy +config.modernmetals.inherited_mode=Die Fantasiestufe wird in Base Metals eingestellt. Nach Änderungen das Spiel neu starten. diff --git a/src/main/resources/assets/modernmetals/lang/en_US.lang b/src/main/resources/assets/modernmetals/lang/en_US.lang index 5e6a7456..59070e88 100644 --- a/src/main/resources/assets/modernmetals/lang/en_US.lang +++ b/src/main/resources/assets/modernmetals/lang/en_US.lang @@ -1586,3 +1586,39 @@ gas.cleanGasTitanium=Clean Titanium Slurry gas.cleanGasTungsten=Clean Tungsten Slurry gas.cleanGasUranium=Clean Uranium Slurry gas.cleanGasZirconium=Clean Zirconium Slurry + +config.modernmetals.title=Modern Metals +config.modernmetals.description=Restart to apply changes. Edit ores in OreSpawn. +config.modernmetals.content_mode=Fantasy Level +config.modernmetals.content_mode.tooltip=High Fantasy keeps all recipes. Low Fantasy restricts unrealistic equipment and structures. Requires a game restart. +high_fantasy=High Fantasy +low_fantasy=Low Fantasy +config.modernmetals.enable_thorium=Enable Thorium +config.modernmetals.enable_boron=Enable Boron +config.modernmetals.enable_beryllium=Enable Beryllium +config.modernmetals.enable_cadmium=Enable Cadmium +config.modernmetals.enable_nichrome=Enable Nichrome +config.modernmetals.enable_aluminum=Enable Aluminum +config.modernmetals.enable_aluminumbrass=Enable Aluminum Brass +config.modernmetals.enable_chromium=Enable Chromium +config.modernmetals.enable_galvanizedsteel=Enable Galvanized Steel +config.modernmetals.enable_iridium=Enable Iridium +config.modernmetals.enable_magnesium=Enable Magnesium +config.modernmetals.enable_manganese=Enable Manganese +config.modernmetals.enable_osmium=Enable Osmium +config.modernmetals.enable_plutonium=Enable Plutonium +config.modernmetals.enable_rutile=Enable Rutile +config.modernmetals.enable_stainlesssteel=Enable Stainless Steel +config.modernmetals.enable_tantalum=Enable Tantalum +config.modernmetals.enable_titanium=Enable Titanium +config.modernmetals.enable_tungsten=Enable Tungsten +config.modernmetals.enable_uranium=Enable Uranium +config.modernmetals.enable_zirconium=Enable Zirconium +config.modernmetals.material.tooltip=Controls which blocks, items and recipes are available for this material. Requires a game restart. +config.modernmetals.villager_trades=Villager Trades +config.modernmetals.villager_trades.tooltip=Let blacksmiths trade Modern Metals ingots and equipment. Requires a game restart. +config.modernmetals.connection.mismatch=The server uses %s, but your game uses %s. Match the Fantasy Level in your mod settings and restart. +config.modernmetals.connection.protocol=Modern Metals could not check the Fantasy Level. Use the same Modern Metals build on the server and client. +config.modernmetals.content_mode.high_fantasy=High Fantasy +config.modernmetals.content_mode.low_fantasy=Low Fantasy +config.modernmetals.inherited_mode=Fantasy Level is set in Base Metals. Restart after changing settings. diff --git a/src/main/resources/assets/modernmetals/lang/en_au.lang b/src/main/resources/assets/modernmetals/lang/en_au.lang index 45a76929..aeaa5eda 100644 --- a/src/main/resources/assets/modernmetals/lang/en_au.lang +++ b/src/main/resources/assets/modernmetals/lang/en_au.lang @@ -1604,3 +1604,39 @@ gas.cleanGasPlatinum=Clean Platinum Slurry gas.cleanGasNickel=Clean Nickel Slurry gas.cleanGasStarsteel=Clean Star-Steel Slurry gas.cleanGasZinc=Clean Zinc Slurry + +config.modernmetals.title=Modern Metals +config.modernmetals.description=Restart to apply changes. Edit ores in OreSpawn. +config.modernmetals.content_mode=Fantasy Level +config.modernmetals.content_mode.tooltip=High Fantasy keeps all recipes. Low Fantasy restricts unrealistic equipment and structures. Requires a game restart. +high_fantasy=High Fantasy +low_fantasy=Low Fantasy +config.modernmetals.enable_thorium=Enable Thorium +config.modernmetals.enable_boron=Enable Boron +config.modernmetals.enable_beryllium=Enable Beryllium +config.modernmetals.enable_cadmium=Enable Cadmium +config.modernmetals.enable_nichrome=Enable Nichrome +config.modernmetals.enable_aluminum=Enable Aluminum +config.modernmetals.enable_aluminumbrass=Enable Aluminum Brass +config.modernmetals.enable_chromium=Enable Chromium +config.modernmetals.enable_galvanizedsteel=Enable Galvanized Steel +config.modernmetals.enable_iridium=Enable Iridium +config.modernmetals.enable_magnesium=Enable Magnesium +config.modernmetals.enable_manganese=Enable Manganese +config.modernmetals.enable_osmium=Enable Osmium +config.modernmetals.enable_plutonium=Enable Plutonium +config.modernmetals.enable_rutile=Enable Rutile +config.modernmetals.enable_stainlesssteel=Enable Stainless Steel +config.modernmetals.enable_tantalum=Enable Tantalum +config.modernmetals.enable_titanium=Enable Titanium +config.modernmetals.enable_tungsten=Enable Tungsten +config.modernmetals.enable_uranium=Enable Uranium +config.modernmetals.enable_zirconium=Enable Zirconium +config.modernmetals.material.tooltip=Controls which blocks, items and recipes are available for this material. Requires a game restart. +config.modernmetals.villager_trades=Villager Trades +config.modernmetals.villager_trades.tooltip=Let blacksmiths trade Modern Metals ingots and equipment. Requires a game restart. +config.modernmetals.connection.mismatch=The server uses %s, but your game uses %s. Match the Fantasy Level in your mod settings and restart. +config.modernmetals.connection.protocol=Modern Metals could not check the Fantasy Level. Use the same Modern Metals build on the server and client. +config.modernmetals.content_mode.high_fantasy=High Fantasy +config.modernmetals.content_mode.low_fantasy=Low Fantasy +config.modernmetals.inherited_mode=Fantasy Level is set in Base Metals. Restart after changing settings. diff --git a/src/main/resources/assets/modernmetals/lang/en_ca.lang b/src/main/resources/assets/modernmetals/lang/en_ca.lang index 45a76929..aeaa5eda 100644 --- a/src/main/resources/assets/modernmetals/lang/en_ca.lang +++ b/src/main/resources/assets/modernmetals/lang/en_ca.lang @@ -1604,3 +1604,39 @@ gas.cleanGasPlatinum=Clean Platinum Slurry gas.cleanGasNickel=Clean Nickel Slurry gas.cleanGasStarsteel=Clean Star-Steel Slurry gas.cleanGasZinc=Clean Zinc Slurry + +config.modernmetals.title=Modern Metals +config.modernmetals.description=Restart to apply changes. Edit ores in OreSpawn. +config.modernmetals.content_mode=Fantasy Level +config.modernmetals.content_mode.tooltip=High Fantasy keeps all recipes. Low Fantasy restricts unrealistic equipment and structures. Requires a game restart. +high_fantasy=High Fantasy +low_fantasy=Low Fantasy +config.modernmetals.enable_thorium=Enable Thorium +config.modernmetals.enable_boron=Enable Boron +config.modernmetals.enable_beryllium=Enable Beryllium +config.modernmetals.enable_cadmium=Enable Cadmium +config.modernmetals.enable_nichrome=Enable Nichrome +config.modernmetals.enable_aluminum=Enable Aluminum +config.modernmetals.enable_aluminumbrass=Enable Aluminum Brass +config.modernmetals.enable_chromium=Enable Chromium +config.modernmetals.enable_galvanizedsteel=Enable Galvanized Steel +config.modernmetals.enable_iridium=Enable Iridium +config.modernmetals.enable_magnesium=Enable Magnesium +config.modernmetals.enable_manganese=Enable Manganese +config.modernmetals.enable_osmium=Enable Osmium +config.modernmetals.enable_plutonium=Enable Plutonium +config.modernmetals.enable_rutile=Enable Rutile +config.modernmetals.enable_stainlesssteel=Enable Stainless Steel +config.modernmetals.enable_tantalum=Enable Tantalum +config.modernmetals.enable_titanium=Enable Titanium +config.modernmetals.enable_tungsten=Enable Tungsten +config.modernmetals.enable_uranium=Enable Uranium +config.modernmetals.enable_zirconium=Enable Zirconium +config.modernmetals.material.tooltip=Controls which blocks, items and recipes are available for this material. Requires a game restart. +config.modernmetals.villager_trades=Villager Trades +config.modernmetals.villager_trades.tooltip=Let blacksmiths trade Modern Metals ingots and equipment. Requires a game restart. +config.modernmetals.connection.mismatch=The server uses %s, but your game uses %s. Match the Fantasy Level in your mod settings and restart. +config.modernmetals.connection.protocol=Modern Metals could not check the Fantasy Level. Use the same Modern Metals build on the server and client. +config.modernmetals.content_mode.high_fantasy=High Fantasy +config.modernmetals.content_mode.low_fantasy=Low Fantasy +config.modernmetals.inherited_mode=Fantasy Level is set in Base Metals. Restart after changing settings. diff --git a/src/main/resources/assets/modernmetals/lang/en_gb.lang b/src/main/resources/assets/modernmetals/lang/en_gb.lang index 1618bbf8..8406d49c 100644 --- a/src/main/resources/assets/modernmetals/lang/en_gb.lang +++ b/src/main/resources/assets/modernmetals/lang/en_gb.lang @@ -1604,3 +1604,39 @@ gas.cleanGasPlatinum=Clean Platinum Slurry gas.cleanGasNickel=Clean Nickel Slurry gas.cleanGasStarsteel=Clean Star-Steel Slurry gas.cleanGasZinc=Clean Zinc Slurry + +config.modernmetals.title=Modern Metals +config.modernmetals.description=Restart to apply changes. Edit ores in OreSpawn. +config.modernmetals.content_mode=Fantasy Level +config.modernmetals.content_mode.tooltip=High Fantasy keeps all recipes. Low Fantasy restricts unrealistic equipment and structures. Requires a game restart. +high_fantasy=High Fantasy +low_fantasy=Low Fantasy +config.modernmetals.enable_thorium=Enable Thorium +config.modernmetals.enable_boron=Enable Boron +config.modernmetals.enable_beryllium=Enable Beryllium +config.modernmetals.enable_cadmium=Enable Cadmium +config.modernmetals.enable_nichrome=Enable Nichrome +config.modernmetals.enable_aluminum=Enable Aluminum +config.modernmetals.enable_aluminumbrass=Enable Aluminum Brass +config.modernmetals.enable_chromium=Enable Chromium +config.modernmetals.enable_galvanizedsteel=Enable Galvanized Steel +config.modernmetals.enable_iridium=Enable Iridium +config.modernmetals.enable_magnesium=Enable Magnesium +config.modernmetals.enable_manganese=Enable Manganese +config.modernmetals.enable_osmium=Enable Osmium +config.modernmetals.enable_plutonium=Enable Plutonium +config.modernmetals.enable_rutile=Enable Rutile +config.modernmetals.enable_stainlesssteel=Enable Stainless Steel +config.modernmetals.enable_tantalum=Enable Tantalum +config.modernmetals.enable_titanium=Enable Titanium +config.modernmetals.enable_tungsten=Enable Tungsten +config.modernmetals.enable_uranium=Enable Uranium +config.modernmetals.enable_zirconium=Enable Zirconium +config.modernmetals.material.tooltip=Controls which blocks, items and recipes are available for this material. Requires a game restart. +config.modernmetals.villager_trades=Villager Trades +config.modernmetals.villager_trades.tooltip=Let blacksmiths trade Modern Metals ingots and equipment. Requires a game restart. +config.modernmetals.connection.mismatch=The server uses %s, but your game uses %s. Match the Fantasy Level in your mod settings and restart. +config.modernmetals.connection.protocol=Modern Metals could not check the Fantasy Level. Use the same Modern Metals build on the server and client. +config.modernmetals.content_mode.high_fantasy=High Fantasy +config.modernmetals.content_mode.low_fantasy=Low Fantasy +config.modernmetals.inherited_mode=Fantasy Level is set in Base Metals. Restart after changing settings. diff --git a/src/main/resources/assets/modernmetals/lang/en_nz.lang b/src/main/resources/assets/modernmetals/lang/en_nz.lang index 45a76929..aeaa5eda 100644 --- a/src/main/resources/assets/modernmetals/lang/en_nz.lang +++ b/src/main/resources/assets/modernmetals/lang/en_nz.lang @@ -1604,3 +1604,39 @@ gas.cleanGasPlatinum=Clean Platinum Slurry gas.cleanGasNickel=Clean Nickel Slurry gas.cleanGasStarsteel=Clean Star-Steel Slurry gas.cleanGasZinc=Clean Zinc Slurry + +config.modernmetals.title=Modern Metals +config.modernmetals.description=Restart to apply changes. Edit ores in OreSpawn. +config.modernmetals.content_mode=Fantasy Level +config.modernmetals.content_mode.tooltip=High Fantasy keeps all recipes. Low Fantasy restricts unrealistic equipment and structures. Requires a game restart. +high_fantasy=High Fantasy +low_fantasy=Low Fantasy +config.modernmetals.enable_thorium=Enable Thorium +config.modernmetals.enable_boron=Enable Boron +config.modernmetals.enable_beryllium=Enable Beryllium +config.modernmetals.enable_cadmium=Enable Cadmium +config.modernmetals.enable_nichrome=Enable Nichrome +config.modernmetals.enable_aluminum=Enable Aluminum +config.modernmetals.enable_aluminumbrass=Enable Aluminum Brass +config.modernmetals.enable_chromium=Enable Chromium +config.modernmetals.enable_galvanizedsteel=Enable Galvanized Steel +config.modernmetals.enable_iridium=Enable Iridium +config.modernmetals.enable_magnesium=Enable Magnesium +config.modernmetals.enable_manganese=Enable Manganese +config.modernmetals.enable_osmium=Enable Osmium +config.modernmetals.enable_plutonium=Enable Plutonium +config.modernmetals.enable_rutile=Enable Rutile +config.modernmetals.enable_stainlesssteel=Enable Stainless Steel +config.modernmetals.enable_tantalum=Enable Tantalum +config.modernmetals.enable_titanium=Enable Titanium +config.modernmetals.enable_tungsten=Enable Tungsten +config.modernmetals.enable_uranium=Enable Uranium +config.modernmetals.enable_zirconium=Enable Zirconium +config.modernmetals.material.tooltip=Controls which blocks, items and recipes are available for this material. Requires a game restart. +config.modernmetals.villager_trades=Villager Trades +config.modernmetals.villager_trades.tooltip=Let blacksmiths trade Modern Metals ingots and equipment. Requires a game restart. +config.modernmetals.connection.mismatch=The server uses %s, but your game uses %s. Match the Fantasy Level in your mod settings and restart. +config.modernmetals.connection.protocol=Modern Metals could not check the Fantasy Level. Use the same Modern Metals build on the server and client. +config.modernmetals.content_mode.high_fantasy=High Fantasy +config.modernmetals.content_mode.low_fantasy=Low Fantasy +config.modernmetals.inherited_mode=Fantasy Level is set in Base Metals. Restart after changing settings. diff --git a/src/main/resources/assets/modernmetals/lang/en_pt.lang b/src/main/resources/assets/modernmetals/lang/en_pt.lang index 00e5cba2..72e6cda2 100644 --- a/src/main/resources/assets/modernmetals/lang/en_pt.lang +++ b/src/main/resources/assets/modernmetals/lang/en_pt.lang @@ -1604,3 +1604,39 @@ gas.cleanGasPlatinum=Clean Platinum Slurry gas.cleanGasNickel=Clean Nickel Slurry gas.cleanGasStarsteel=Clean Star-Steel Slurry gas.cleanGasZinc=Clean Zinc Slurry + +config.modernmetals.title=Modern Metals +config.modernmetals.description=Restart to apply changes. Edit ores in OreSpawn. +config.modernmetals.content_mode=Fantasy Level +config.modernmetals.content_mode.tooltip=High Fantasy keeps all recipes. Low Fantasy restricts unrealistic equipment and structures. Requires a game restart. +high_fantasy=High Fantasy +low_fantasy=Low Fantasy +config.modernmetals.enable_thorium=Enable Thorium +config.modernmetals.enable_boron=Enable Boron +config.modernmetals.enable_beryllium=Enable Beryllium +config.modernmetals.enable_cadmium=Enable Cadmium +config.modernmetals.enable_nichrome=Enable Nichrome +config.modernmetals.enable_aluminum=Enable Aluminum +config.modernmetals.enable_aluminumbrass=Enable Aluminum Brass +config.modernmetals.enable_chromium=Enable Chromium +config.modernmetals.enable_galvanizedsteel=Enable Galvanized Steel +config.modernmetals.enable_iridium=Enable Iridium +config.modernmetals.enable_magnesium=Enable Magnesium +config.modernmetals.enable_manganese=Enable Manganese +config.modernmetals.enable_osmium=Enable Osmium +config.modernmetals.enable_plutonium=Enable Plutonium +config.modernmetals.enable_rutile=Enable Rutile +config.modernmetals.enable_stainlesssteel=Enable Stainless Steel +config.modernmetals.enable_tantalum=Enable Tantalum +config.modernmetals.enable_titanium=Enable Titanium +config.modernmetals.enable_tungsten=Enable Tungsten +config.modernmetals.enable_uranium=Enable Uranium +config.modernmetals.enable_zirconium=Enable Zirconium +config.modernmetals.material.tooltip=Controls which blocks, items and recipes are available for this material. Requires a game restart. +config.modernmetals.villager_trades=Villager Trades +config.modernmetals.villager_trades.tooltip=Let blacksmiths trade Modern Metals ingots and equipment. Requires a game restart. +config.modernmetals.connection.mismatch=The server uses %s, but your game uses %s. Match the Fantasy Level in your mod settings and restart. +config.modernmetals.connection.protocol=Modern Metals could not check the Fantasy Level. Use the same Modern Metals build on the server and client. +config.modernmetals.content_mode.high_fantasy=High Fantasy +config.modernmetals.content_mode.low_fantasy=Low Fantasy +config.modernmetals.inherited_mode=Fantasy Level is set in Base Metals. Restart after changing settings. diff --git a/src/main/resources/assets/modernmetals/lang/ru_RU.lang b/src/main/resources/assets/modernmetals/lang/ru_RU.lang index 281225a1..9afd2809 100644 --- a/src/main/resources/assets/modernmetals/lang/ru_RU.lang +++ b/src/main/resources/assets/modernmetals/lang/ru_RU.lang @@ -1511,3 +1511,39 @@ tile.modernmetals.thorium_pressure_plate.name=Thorium Pressure Plate tile.modernmetals.thorium_slab.name=Thorium Slab tile.modernmetals.thorium_stairs.name=Thorium Stairs tile.modernmetals.thorium_wall.name=Thorium Wall + +config.modernmetals.material.tooltip=Включает блоки, предметы и рецепты этого материала. Требуется перезапуск игры. +config.modernmetals.title=Modern Metals +config.modernmetals.description=Для применения нужен перезапуск. Руды настраиваются в OreSpawn. +config.modernmetals.content_mode=Уровень фэнтези +config.modernmetals.content_mode.tooltip=Высокий уровень сохраняет все рецепты. Низкий ограничивает нереалистичное снаряжение и строительные блоки. Требуется перезапуск игры. +high_fantasy=Высокий уровень фэнтези +low_fantasy=Низкий уровень фэнтези +config.modernmetals.villager_trades=Торговля с жителями +config.modernmetals.villager_trades.tooltip=Кузнецы могут торговать слитками и снаряжением Modern Metals. Требуется перезапуск игры. +config.modernmetals.connection.mismatch=Уровень на сервере — %s, в вашей игре — %s. Выберите тот же уровень в меню «Моды > Modern Metals > Настройки» и перезапустите игру. +config.modernmetals.connection.protocol=Не удалось проверить уровень фэнтези Modern Metals. Установите одну и ту же сборку Modern Metals на клиенте и сервере. +config.modernmetals.enable_thorium=Включить торий +config.modernmetals.enable_boron=Включить бор +config.modernmetals.enable_beryllium=Включить бериллий +config.modernmetals.enable_cadmium=Включить кадмий +config.modernmetals.enable_nichrome=Включить нихром +config.modernmetals.enable_aluminum=Включить алюминий +config.modernmetals.enable_aluminumbrass=Включить алюминиевую латунь +config.modernmetals.enable_chromium=Включить хром +config.modernmetals.enable_galvanizedsteel=Включить оцинкованную сталь +config.modernmetals.enable_iridium=Включить иридий +config.modernmetals.enable_magnesium=Включить магний +config.modernmetals.enable_manganese=Включить марганец +config.modernmetals.enable_osmium=Включить осмий +config.modernmetals.enable_plutonium=Включить плутоний +config.modernmetals.enable_rutile=Включить рутил +config.modernmetals.enable_stainlesssteel=Включить нержавеющую сталь +config.modernmetals.enable_tantalum=Включить тантал +config.modernmetals.enable_titanium=Включить титан +config.modernmetals.enable_tungsten=Включить вольфрам +config.modernmetals.enable_uranium=Включить уран +config.modernmetals.enable_zirconium=Включить цирконий +config.modernmetals.content_mode.high_fantasy=Высокий уровень фэнтези +config.modernmetals.content_mode.low_fantasy=Низкий уровень фэнтези +config.modernmetals.inherited_mode=Уровень фэнтези задаётся в Base Metals. Перезапустите игру после изменений. diff --git a/src/main/resources/assets/modernmetals/lang/zh_CN.lang b/src/main/resources/assets/modernmetals/lang/zh_CN.lang index 57e1b4f6..be30efe2 100644 --- a/src/main/resources/assets/modernmetals/lang/zh_CN.lang +++ b/src/main/resources/assets/modernmetals/lang/zh_CN.lang @@ -1512,4 +1512,38 @@ tile.modernmetals.thorium_slab.name=钍台阶 tile.modernmetals.thorium_stairs.name=钍楼梯 tile.modernmetals.thorium_wall.name=钍墙 - +config.modernmetals.material.tooltip=控制是否启用此材料的方块、物品和配方。需要重启游戏。 +config.modernmetals.title=Modern Metals +config.modernmetals.description=重启游戏后应用更改。请在 OreSpawn 中设置矿石生成。 +config.modernmetals.content_mode=奇幻等级 +config.modernmetals.content_mode.tooltip=高奇幻保留全部配方。低奇幻限制不符合现实的装备和建筑部件。需要重启游戏。 +high_fantasy=高奇幻 +low_fantasy=低奇幻 +config.modernmetals.villager_trades=村民交易 +config.modernmetals.villager_trades.tooltip=允许铁匠交易 Modern Metals 的金属锭和装备。需要重启游戏。 +config.modernmetals.connection.mismatch=服务器使用%s,你的游戏使用%s。请在模组 > Modern Metals > 配置中选择与服务器一致的奇幻等级,然后重启游戏。 +config.modernmetals.connection.protocol=Modern Metals 无法核对奇幻等级。请在服务器和客户端使用相同构建版本的 Modern Metals。 +config.modernmetals.enable_thorium=启用钍 +config.modernmetals.enable_boron=启用硼 +config.modernmetals.enable_beryllium=启用铍 +config.modernmetals.enable_cadmium=启用镉 +config.modernmetals.enable_nichrome=启用镍铬合金 +config.modernmetals.enable_aluminum=启用铝 +config.modernmetals.enable_aluminumbrass=启用铝黄铜 +config.modernmetals.enable_chromium=启用铬 +config.modernmetals.enable_galvanizedsteel=启用镀锌钢 +config.modernmetals.enable_iridium=启用铱 +config.modernmetals.enable_magnesium=启用镁 +config.modernmetals.enable_manganese=启用锰 +config.modernmetals.enable_osmium=启用锇 +config.modernmetals.enable_plutonium=启用钚 +config.modernmetals.enable_rutile=启用金红石 +config.modernmetals.enable_stainlesssteel=启用不锈钢 +config.modernmetals.enable_tantalum=启用钽 +config.modernmetals.enable_titanium=启用钛 +config.modernmetals.enable_tungsten=启用钨 +config.modernmetals.enable_uranium=启用铀 +config.modernmetals.enable_zirconium=启用锆 +config.modernmetals.content_mode.high_fantasy=高奇幻 +config.modernmetals.content_mode.low_fantasy=低奇幻 +config.modernmetals.inherited_mode=幻想等级由 Base Metals 设置。更改设置后请重新启动游戏。 diff --git a/src/main/resources/assets/modernmetals/models/block/aluminum_ore.json b/src/main/resources/assets/modernmetals/models/block/aluminum_ore.json deleted file mode 100644 index 4616b535..00000000 --- a/src/main/resources/assets/modernmetals/models/block/aluminum_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/aluminum_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/aluminum_plate.json b/src/main/resources/assets/modernmetals/models/block/aluminum_plate.json index 47e435b1..ea91a282 100644 --- a/src/main/resources/assets/modernmetals/models/block/aluminum_plate.json +++ b/src/main/resources/assets/modernmetals/models/block/aluminum_plate.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:block/plate", + "parent": "modernmetals:block/plate", "textures": { "top": "modernmetals:blocks/aluminum_plate", "side": "modernmetals:blocks/aluminum_plate", diff --git a/src/main/resources/assets/modernmetals/models/block/aluminumbrass_ore.json b/src/main/resources/assets/modernmetals/models/block/aluminumbrass_ore.json deleted file mode 100644 index ed015015..00000000 --- a/src/main/resources/assets/modernmetals/models/block/aluminumbrass_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/aluminumbrass_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/aluminumbrass_plate.json b/src/main/resources/assets/modernmetals/models/block/aluminumbrass_plate.json index 681ab3c1..3dd5912a 100644 --- a/src/main/resources/assets/modernmetals/models/block/aluminumbrass_plate.json +++ b/src/main/resources/assets/modernmetals/models/block/aluminumbrass_plate.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:block/plate", + "parent": "modernmetals:block/plate", "textures": { "top": "modernmetals:blocks/aluminumbrass_plate", "side": "modernmetals:blocks/aluminumbrass_plate", diff --git a/src/main/resources/assets/modernmetals/models/block/beryllium_ore.json b/src/main/resources/assets/modernmetals/models/block/beryllium_ore.json deleted file mode 100644 index 6e531aa2..00000000 --- a/src/main/resources/assets/modernmetals/models/block/beryllium_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/beryllium_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/beryllium_plate.json b/src/main/resources/assets/modernmetals/models/block/beryllium_plate.json index 64fd0908..b0107a3f 100644 --- a/src/main/resources/assets/modernmetals/models/block/beryllium_plate.json +++ b/src/main/resources/assets/modernmetals/models/block/beryllium_plate.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:block/plate", + "parent": "modernmetals:block/plate", "textures": { "top": "modernmetals:blocks/beryllium_plate", "side": "modernmetals:blocks/beryllium_plate", diff --git a/src/main/resources/assets/modernmetals/models/block/boron_ore.json b/src/main/resources/assets/modernmetals/models/block/boron_ore.json deleted file mode 100644 index 13dfb423..00000000 --- a/src/main/resources/assets/modernmetals/models/block/boron_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/boron_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/boron_plate.json b/src/main/resources/assets/modernmetals/models/block/boron_plate.json index 52ef6b24..bf915982 100644 --- a/src/main/resources/assets/modernmetals/models/block/boron_plate.json +++ b/src/main/resources/assets/modernmetals/models/block/boron_plate.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:block/plate", + "parent": "modernmetals:block/plate", "textures": { "top": "modernmetals:blocks/boron_plate", "side": "modernmetals:blocks/boron_plate", diff --git a/src/main/resources/assets/modernmetals/models/block/cadmium_ore.json b/src/main/resources/assets/modernmetals/models/block/cadmium_ore.json deleted file mode 100644 index cf3636e0..00000000 --- a/src/main/resources/assets/modernmetals/models/block/cadmium_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/cadmium_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/cadmium_plate.json b/src/main/resources/assets/modernmetals/models/block/cadmium_plate.json index 773edbe1..2195e396 100644 --- a/src/main/resources/assets/modernmetals/models/block/cadmium_plate.json +++ b/src/main/resources/assets/modernmetals/models/block/cadmium_plate.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:block/plate", + "parent": "modernmetals:block/plate", "textures": { "top": "modernmetals:blocks/cadmium_plate", "side": "modernmetals:blocks/cadmium_plate", diff --git a/src/main/resources/assets/modernmetals/models/block/chromium_ore.json b/src/main/resources/assets/modernmetals/models/block/chromium_ore.json deleted file mode 100644 index f867a0b3..00000000 --- a/src/main/resources/assets/modernmetals/models/block/chromium_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/chromium_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/chromium_plate.json b/src/main/resources/assets/modernmetals/models/block/chromium_plate.json index 993dcf27..1f85aaf3 100644 --- a/src/main/resources/assets/modernmetals/models/block/chromium_plate.json +++ b/src/main/resources/assets/modernmetals/models/block/chromium_plate.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:block/plate", + "parent": "modernmetals:block/plate", "textures": { "top": "modernmetals:blocks/chromium_plate", "side": "modernmetals:blocks/chromium_plate", diff --git a/src/main/resources/assets/modernmetals/models/block/galvanizedsteel_ore.json b/src/main/resources/assets/modernmetals/models/block/galvanizedsteel_ore.json deleted file mode 100644 index f7e8487e..00000000 --- a/src/main/resources/assets/modernmetals/models/block/galvanizedsteel_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/galvanizedsteel_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/galvanizedsteel_plate.json b/src/main/resources/assets/modernmetals/models/block/galvanizedsteel_plate.json index 0e542f0c..76206966 100644 --- a/src/main/resources/assets/modernmetals/models/block/galvanizedsteel_plate.json +++ b/src/main/resources/assets/modernmetals/models/block/galvanizedsteel_plate.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:block/plate", + "parent": "modernmetals:block/plate", "textures": { "top": "modernmetals:blocks/galvanizedsteel_plate", "side": "modernmetals:blocks/galvanizedsteel_plate", diff --git a/src/main/resources/assets/modernmetals/models/block/iridium_ore.json b/src/main/resources/assets/modernmetals/models/block/iridium_ore.json deleted file mode 100644 index b3bd98cb..00000000 --- a/src/main/resources/assets/modernmetals/models/block/iridium_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/iridium_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/iridium_plate.json b/src/main/resources/assets/modernmetals/models/block/iridium_plate.json index 68582f0d..f9899b27 100644 --- a/src/main/resources/assets/modernmetals/models/block/iridium_plate.json +++ b/src/main/resources/assets/modernmetals/models/block/iridium_plate.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:block/plate", + "parent": "modernmetals:block/plate", "textures": { "top": "modernmetals:blocks/iridium_plate", "side": "modernmetals:blocks/iridium_plate", diff --git a/src/main/resources/assets/modernmetals/models/block/magnesium_ore.json b/src/main/resources/assets/modernmetals/models/block/magnesium_ore.json deleted file mode 100644 index 9f6fb257..00000000 --- a/src/main/resources/assets/modernmetals/models/block/magnesium_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/magnesium_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/magnesium_plate.json b/src/main/resources/assets/modernmetals/models/block/magnesium_plate.json index 09e617a1..daabb200 100644 --- a/src/main/resources/assets/modernmetals/models/block/magnesium_plate.json +++ b/src/main/resources/assets/modernmetals/models/block/magnesium_plate.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:block/plate", + "parent": "modernmetals:block/plate", "textures": { "top": "modernmetals:blocks/magnesium_plate", "side": "modernmetals:blocks/magnesium_plate", diff --git a/src/main/resources/assets/modernmetals/models/block/manganese_ore.json b/src/main/resources/assets/modernmetals/models/block/manganese_ore.json deleted file mode 100644 index 6c79d985..00000000 --- a/src/main/resources/assets/modernmetals/models/block/manganese_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/manganese_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/manganese_plate.json b/src/main/resources/assets/modernmetals/models/block/manganese_plate.json index 42c25245..821f8327 100644 --- a/src/main/resources/assets/modernmetals/models/block/manganese_plate.json +++ b/src/main/resources/assets/modernmetals/models/block/manganese_plate.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:block/plate", + "parent": "modernmetals:block/plate", "textures": { "top": "modernmetals:blocks/manganese_plate", "side": "modernmetals:blocks/manganese_plate", diff --git a/src/main/resources/assets/modernmetals/models/block/nichrome_ore.json b/src/main/resources/assets/modernmetals/models/block/nichrome_ore.json deleted file mode 100644 index 88f7cf51..00000000 --- a/src/main/resources/assets/modernmetals/models/block/nichrome_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/nichrome_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/nichrome_plate.json b/src/main/resources/assets/modernmetals/models/block/nichrome_plate.json index c5ec7e5e..1d07b3aa 100644 --- a/src/main/resources/assets/modernmetals/models/block/nichrome_plate.json +++ b/src/main/resources/assets/modernmetals/models/block/nichrome_plate.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:block/plate", + "parent": "modernmetals:block/plate", "textures": { "top": "modernmetals:blocks/nichrome_plate", "side": "modernmetals:blocks/nichrome_plate", diff --git a/src/main/resources/assets/modernmetals/models/block/osmium_ore.json b/src/main/resources/assets/modernmetals/models/block/osmium_ore.json deleted file mode 100644 index 9aabe0d0..00000000 --- a/src/main/resources/assets/modernmetals/models/block/osmium_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/osmium_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/osmium_plate.json b/src/main/resources/assets/modernmetals/models/block/osmium_plate.json index 6fc22243..131f1cee 100644 --- a/src/main/resources/assets/modernmetals/models/block/osmium_plate.json +++ b/src/main/resources/assets/modernmetals/models/block/osmium_plate.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:block/plate", + "parent": "modernmetals:block/plate", "textures": { "top": "modernmetals:blocks/osmium_plate", "side": "modernmetals:blocks/osmium_plate", diff --git a/src/main/resources/assets/modernmetals/models/block/plate.json b/src/main/resources/assets/modernmetals/models/block/plate.json new file mode 100644 index 00000000..1c963d8c --- /dev/null +++ b/src/main/resources/assets/modernmetals/models/block/plate.json @@ -0,0 +1,50 @@ +{ + "textures": { + "particle": "#top" + }, + "elements": [ + { "from": [ 0, 0, 15 ], + "to": [ 16, 16, 16 ], + "faces": { + "down": { "uv": [ 0, 0, 16, 1 ], "texture": "#side", "cullface": "down" }, + "up": { "uv": [ 0, 0, 16, 1 ], "texture": "#side", "cullface": "up" }, + "west": { "uv": [15, 0, 16, 16 ], "texture": "#side", "cullface": "west" }, + "east": { "uv": [15, 0, 16, 16 ], "texture": "#side", "cullface": "east" }, + "north": { "uv": [ 0, 0, 16, 16 ], "texture": "#bottom", "cullface": "north"}, + "south": { "uv": [ 0, 0, 16, 16 ], "texture": "#top" } + } + } + ], + "display": { + "gui": { + "rotation": [ 0, 0, 0 ], + "translation": [ 0, 0, 0], + "scale":[ 0.625, 0.625, 0.625 ] + }, + "ground": { + "rotation": [ 0, 0, 0 ], + "translation": [ 0, 3, 0], + "scale":[ 0.25, 0.25, 0.25 ] + }, + "fixed": { + "rotation": [ 0, 0, 0 ], + "translation": [ 0, 0, 0], + "scale":[ 0.5, 0.5, 0.5 ] + }, + "thirdperson_righthand": { + "rotation": [ 75, 45, 0 ], + "translation": [ 0, 2.5, 0], + "scale": [ 0.375, 0.375, 0.375 ] + }, + "firstperson_righthand": { + "rotation": [ 0, 45, 0 ], + "translation": [ 0, 0, 0 ], + "scale": [ 0.40, 0.40, 0.40 ] + }, + "firstperson_lefthand": { + "rotation": [ 0, 225, 0 ], + "translation": [ 0, 0, 0 ], + "scale": [ 0.40, 0.40, 0.40 ] + } + } +} diff --git a/src/main/resources/assets/modernmetals/models/block/plutonium_ore.json b/src/main/resources/assets/modernmetals/models/block/plutonium_ore.json deleted file mode 100644 index 2e4233ea..00000000 --- a/src/main/resources/assets/modernmetals/models/block/plutonium_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/plutonium_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/plutonium_plate.json b/src/main/resources/assets/modernmetals/models/block/plutonium_plate.json index c26563c7..80b64346 100644 --- a/src/main/resources/assets/modernmetals/models/block/plutonium_plate.json +++ b/src/main/resources/assets/modernmetals/models/block/plutonium_plate.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:block/plate", + "parent": "modernmetals:block/plate", "textures": { "top": "modernmetals:blocks/plutonium_plate", "side": "modernmetals:blocks/plutonium_plate", diff --git a/src/main/resources/assets/modernmetals/models/block/rutile_ore.json b/src/main/resources/assets/modernmetals/models/block/rutile_ore.json deleted file mode 100644 index 29f9be6c..00000000 --- a/src/main/resources/assets/modernmetals/models/block/rutile_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/rutile_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/rutile_plate.json b/src/main/resources/assets/modernmetals/models/block/rutile_plate.json index 5eaac81d..2d635302 100644 --- a/src/main/resources/assets/modernmetals/models/block/rutile_plate.json +++ b/src/main/resources/assets/modernmetals/models/block/rutile_plate.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:block/plate", + "parent": "modernmetals:block/plate", "textures": { "top": "modernmetals:blocks/rutile_plate", "side": "modernmetals:blocks/rutile_plate", diff --git a/src/main/resources/assets/modernmetals/models/block/stainlesssteel_ore.json b/src/main/resources/assets/modernmetals/models/block/stainlesssteel_ore.json deleted file mode 100644 index e757848f..00000000 --- a/src/main/resources/assets/modernmetals/models/block/stainlesssteel_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/stainlesssteel_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/stainlesssteel_plate.json b/src/main/resources/assets/modernmetals/models/block/stainlesssteel_plate.json index 5e00dfa1..22855f67 100644 --- a/src/main/resources/assets/modernmetals/models/block/stainlesssteel_plate.json +++ b/src/main/resources/assets/modernmetals/models/block/stainlesssteel_plate.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:block/plate", + "parent": "modernmetals:block/plate", "textures": { "top": "modernmetals:blocks/stainlesssteel_plate", "side": "modernmetals:blocks/stainlesssteel_plate", diff --git a/src/main/resources/assets/modernmetals/models/block/tantalum_ore.json b/src/main/resources/assets/modernmetals/models/block/tantalum_ore.json deleted file mode 100644 index 4aae5e70..00000000 --- a/src/main/resources/assets/modernmetals/models/block/tantalum_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/tantalum_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/tantalum_plate.json b/src/main/resources/assets/modernmetals/models/block/tantalum_plate.json index 8351801c..cad8623e 100644 --- a/src/main/resources/assets/modernmetals/models/block/tantalum_plate.json +++ b/src/main/resources/assets/modernmetals/models/block/tantalum_plate.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:block/plate", + "parent": "modernmetals:block/plate", "textures": { "top": "modernmetals:blocks/tantalum_plate", "side": "modernmetals:blocks/tantalum_plate", diff --git a/src/main/resources/assets/modernmetals/models/block/thorium_plate.json b/src/main/resources/assets/modernmetals/models/block/thorium_plate.json index 533e429d..73eb98c8 100644 --- a/src/main/resources/assets/modernmetals/models/block/thorium_plate.json +++ b/src/main/resources/assets/modernmetals/models/block/thorium_plate.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:block/plate", + "parent": "modernmetals:block/plate", "textures": { "top": "modernmetals:blocks/thorium_plate", "side": "modernmetals:blocks/thorium_plate", diff --git a/src/main/resources/assets/modernmetals/models/block/titanium_ore.json b/src/main/resources/assets/modernmetals/models/block/titanium_ore.json deleted file mode 100644 index ddfbdce7..00000000 --- a/src/main/resources/assets/modernmetals/models/block/titanium_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/titanium_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/titanium_plate.json b/src/main/resources/assets/modernmetals/models/block/titanium_plate.json index c701e591..6e71e038 100644 --- a/src/main/resources/assets/modernmetals/models/block/titanium_plate.json +++ b/src/main/resources/assets/modernmetals/models/block/titanium_plate.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:block/plate", + "parent": "modernmetals:block/plate", "textures": { "top": "modernmetals:blocks/titanium_plate", "side": "modernmetals:blocks/titanium_plate", diff --git a/src/main/resources/assets/modernmetals/models/block/tungsten_ore.json b/src/main/resources/assets/modernmetals/models/block/tungsten_ore.json deleted file mode 100644 index 369e7432..00000000 --- a/src/main/resources/assets/modernmetals/models/block/tungsten_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/tungsten_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/tungsten_plate.json b/src/main/resources/assets/modernmetals/models/block/tungsten_plate.json index fd968bed..245a0889 100644 --- a/src/main/resources/assets/modernmetals/models/block/tungsten_plate.json +++ b/src/main/resources/assets/modernmetals/models/block/tungsten_plate.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:block/plate", + "parent": "modernmetals:block/plate", "textures": { "top": "modernmetals:blocks/tungsten_plate", "side": "modernmetals:blocks/tungsten_plate", diff --git a/src/main/resources/assets/modernmetals/models/block/uranium_ore.json b/src/main/resources/assets/modernmetals/models/block/uranium_ore.json deleted file mode 100644 index b0dadbef..00000000 --- a/src/main/resources/assets/modernmetals/models/block/uranium_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/uranium_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/uranium_plate.json b/src/main/resources/assets/modernmetals/models/block/uranium_plate.json index 86a0730d..c85823cf 100644 --- a/src/main/resources/assets/modernmetals/models/block/uranium_plate.json +++ b/src/main/resources/assets/modernmetals/models/block/uranium_plate.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:block/plate", + "parent": "modernmetals:block/plate", "textures": { "top": "modernmetals:blocks/uranium_plate", "side": "modernmetals:blocks/uranium_plate", diff --git a/src/main/resources/assets/modernmetals/models/block/zirconium_ore.json b/src/main/resources/assets/modernmetals/models/block/zirconium_ore.json deleted file mode 100644 index c5835215..00000000 --- a/src/main/resources/assets/modernmetals/models/block/zirconium_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "modernmetals:blocks/zirconium_ore" - } -} diff --git a/src/main/resources/assets/modernmetals/models/block/zirconium_plate.json b/src/main/resources/assets/modernmetals/models/block/zirconium_plate.json index 2f4b4803..7e0b7856 100644 --- a/src/main/resources/assets/modernmetals/models/block/zirconium_plate.json +++ b/src/main/resources/assets/modernmetals/models/block/zirconium_plate.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:block/plate", + "parent": "modernmetals:block/plate", "textures": { "top": "modernmetals:blocks/zirconium_plate", "side": "modernmetals:blocks/zirconium_plate", diff --git a/src/main/resources/assets/modernmetals/models/item/aluminum_door.json b/src/main/resources/assets/modernmetals/models/item/aluminum_door.json index b36cbcc3..bba6d84c 100644 --- a/src/main/resources/assets/modernmetals/models/item/aluminum_door.json +++ b/src/main/resources/assets/modernmetals/models/item/aluminum_door.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/aluminum_door_lower", "top": "modernmetals:blocks/aluminum_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/aluminum_door_item.json b/src/main/resources/assets/modernmetals/models/item/aluminum_door_item.json index b36cbcc3..bba6d84c 100644 --- a/src/main/resources/assets/modernmetals/models/item/aluminum_door_item.json +++ b/src/main/resources/assets/modernmetals/models/item/aluminum_door_item.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/aluminum_door_lower", "top": "modernmetals:blocks/aluminum_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/aluminumbrass_door.json b/src/main/resources/assets/modernmetals/models/item/aluminumbrass_door.json index fd42e5e5..8a7485b0 100644 --- a/src/main/resources/assets/modernmetals/models/item/aluminumbrass_door.json +++ b/src/main/resources/assets/modernmetals/models/item/aluminumbrass_door.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/aluminumbrass_door_lower", "top": "modernmetals:blocks/aluminumbrass_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/aluminumbrass_door_item.json b/src/main/resources/assets/modernmetals/models/item/aluminumbrass_door_item.json index fd42e5e5..8a7485b0 100644 --- a/src/main/resources/assets/modernmetals/models/item/aluminumbrass_door_item.json +++ b/src/main/resources/assets/modernmetals/models/item/aluminumbrass_door_item.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/aluminumbrass_door_lower", "top": "modernmetals:blocks/aluminumbrass_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/beryllium_door.json b/src/main/resources/assets/modernmetals/models/item/beryllium_door.json index 397b8262..322575b2 100644 --- a/src/main/resources/assets/modernmetals/models/item/beryllium_door.json +++ b/src/main/resources/assets/modernmetals/models/item/beryllium_door.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/beryllium_door_lower", "top": "modernmetals:blocks/beryllium_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/beryllium_door_item.json b/src/main/resources/assets/modernmetals/models/item/beryllium_door_item.json index 397b8262..322575b2 100644 --- a/src/main/resources/assets/modernmetals/models/item/beryllium_door_item.json +++ b/src/main/resources/assets/modernmetals/models/item/beryllium_door_item.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/beryllium_door_lower", "top": "modernmetals:blocks/beryllium_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/boron_door.json b/src/main/resources/assets/modernmetals/models/item/boron_door.json index 2401e22a..b68d2d42 100644 --- a/src/main/resources/assets/modernmetals/models/item/boron_door.json +++ b/src/main/resources/assets/modernmetals/models/item/boron_door.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/boron_door_lower", "top": "modernmetals:blocks/boron_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/boron_door_item.json b/src/main/resources/assets/modernmetals/models/item/boron_door_item.json index 2401e22a..b68d2d42 100644 --- a/src/main/resources/assets/modernmetals/models/item/boron_door_item.json +++ b/src/main/resources/assets/modernmetals/models/item/boron_door_item.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/boron_door_lower", "top": "modernmetals:blocks/boron_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/cadmium_door.json b/src/main/resources/assets/modernmetals/models/item/cadmium_door.json index 3680748e..b52b105f 100644 --- a/src/main/resources/assets/modernmetals/models/item/cadmium_door.json +++ b/src/main/resources/assets/modernmetals/models/item/cadmium_door.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/cadmium_door_lower", "top": "modernmetals:blocks/cadmium_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/cadmium_door_item.json b/src/main/resources/assets/modernmetals/models/item/cadmium_door_item.json index 3680748e..b52b105f 100644 --- a/src/main/resources/assets/modernmetals/models/item/cadmium_door_item.json +++ b/src/main/resources/assets/modernmetals/models/item/cadmium_door_item.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/cadmium_door_lower", "top": "modernmetals:blocks/cadmium_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/chromium_door.json b/src/main/resources/assets/modernmetals/models/item/chromium_door.json index 419b4c2c..ed62953a 100644 --- a/src/main/resources/assets/modernmetals/models/item/chromium_door.json +++ b/src/main/resources/assets/modernmetals/models/item/chromium_door.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/chromium_door_lower", "top": "modernmetals:blocks/chromium_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/chromium_door_item.json b/src/main/resources/assets/modernmetals/models/item/chromium_door_item.json index 419b4c2c..ed62953a 100644 --- a/src/main/resources/assets/modernmetals/models/item/chromium_door_item.json +++ b/src/main/resources/assets/modernmetals/models/item/chromium_door_item.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/chromium_door_lower", "top": "modernmetals:blocks/chromium_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/door_item.json b/src/main/resources/assets/modernmetals/models/item/door_item.json new file mode 100644 index 00000000..006e7ec3 --- /dev/null +++ b/src/main/resources/assets/modernmetals/models/item/door_item.json @@ -0,0 +1,57 @@ +{ + "ambientocclusion": false, + "elements": [ + { "from": [ 0, 0, 4 ], + "to": [ 1, 8, 12 ], + "faces": { + "down": { "uv": [ 13, 0, 16, 16 ], "texture": "#bottom" }, + "north": { "uv": [ 3, 0, 0, 16 ], "texture": "#bottom" }, + "south": { "uv": [ 0, 0, 3, 16 ], "texture": "#bottom" }, + "west": { "uv": [ 0, 0, 16, 16 ], "texture": "#bottom" }, + "east": { "uv": [ 16, 0, 0, 16 ], "texture": "#bottom" } + } + }, + { "from": [ 0, 8, 4 ], + "to": [ 1, 16, 12 ], + "faces": { + "up": { "uv": [ 13, 0, 16, 16 ], "texture": "#bottom"}, + "north": { "uv": [ 3, 0, 0, 16 ], "texture": "#top" }, + "south": { "uv": [ 0, 0, 3, 16 ], "texture": "#top" }, + "west": { "uv": [ 0, 0, 16, 16 ], "texture": "#top" }, + "east": { "uv": [ 16, 0, 0, 16 ], "texture": "#top" } + } + } + ], + "display": { + "gui": { + "rotation": [ 0, 90, 0 ], + "translation": [ 0, 0, 0], + "scale":[ 0.9, 0.9, 0.9 ] + }, + "ground": { + "rotation": [ 0, 0, 0 ], + "translation": [ 0, 3, 0], + "scale":[ 0.25, 0.25, 0.25 ] + }, + "fixed": { + "rotation": [ 0, 0, 0 ], + "translation": [ 0, 0, 0], + "scale":[ 0.5, 0.5, 0.5 ] + }, + "thirdperson_righthand": { + "rotation": [ 75, 45, 0 ], + "translation": [ 0, 2.5, 0], + "scale": [ 0.5, 0.5, 0.5 ] + }, + "firstperson_righthand": { + "rotation": [ 0, 45, 0 ], + "translation": [ 0, 0, 0 ], + "scale": [ 0.40, 0.40, 0.40 ] + }, + "firstperson_lefthand": { + "rotation": [ 0, 225, 0 ], + "translation": [ 0, 0, 0 ], + "scale": [ 0.40, 0.40, 0.40 ] + } + } +} diff --git a/src/main/resources/assets/modernmetals/models/item/galvanizedsteel_door.json b/src/main/resources/assets/modernmetals/models/item/galvanizedsteel_door.json index 98b2e3cc..79eca706 100644 --- a/src/main/resources/assets/modernmetals/models/item/galvanizedsteel_door.json +++ b/src/main/resources/assets/modernmetals/models/item/galvanizedsteel_door.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/galvanizedsteel_door_lower", "top": "modernmetals:blocks/galvanizedsteel_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/galvanizedsteel_door_item.json b/src/main/resources/assets/modernmetals/models/item/galvanizedsteel_door_item.json index 98b2e3cc..79eca706 100644 --- a/src/main/resources/assets/modernmetals/models/item/galvanizedsteel_door_item.json +++ b/src/main/resources/assets/modernmetals/models/item/galvanizedsteel_door_item.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/galvanizedsteel_door_lower", "top": "modernmetals:blocks/galvanizedsteel_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/iridium_door.json b/src/main/resources/assets/modernmetals/models/item/iridium_door.json index 8fa62811..a3825527 100644 --- a/src/main/resources/assets/modernmetals/models/item/iridium_door.json +++ b/src/main/resources/assets/modernmetals/models/item/iridium_door.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/iridium_door_lower", "top": "modernmetals:blocks/iridium_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/iridium_door_item.json b/src/main/resources/assets/modernmetals/models/item/iridium_door_item.json index 8fa62811..a3825527 100644 --- a/src/main/resources/assets/modernmetals/models/item/iridium_door_item.json +++ b/src/main/resources/assets/modernmetals/models/item/iridium_door_item.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/iridium_door_lower", "top": "modernmetals:blocks/iridium_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/magnesium_door.json b/src/main/resources/assets/modernmetals/models/item/magnesium_door.json index 1f738a53..a9ab4bee 100644 --- a/src/main/resources/assets/modernmetals/models/item/magnesium_door.json +++ b/src/main/resources/assets/modernmetals/models/item/magnesium_door.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/magnesium_door_lower", "top": "modernmetals:blocks/magnesium_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/magnesium_door_item.json b/src/main/resources/assets/modernmetals/models/item/magnesium_door_item.json index 1f738a53..a9ab4bee 100644 --- a/src/main/resources/assets/modernmetals/models/item/magnesium_door_item.json +++ b/src/main/resources/assets/modernmetals/models/item/magnesium_door_item.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/magnesium_door_lower", "top": "modernmetals:blocks/magnesium_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/manganese_door.json b/src/main/resources/assets/modernmetals/models/item/manganese_door.json index 9cf555dc..a045787f 100644 --- a/src/main/resources/assets/modernmetals/models/item/manganese_door.json +++ b/src/main/resources/assets/modernmetals/models/item/manganese_door.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/manganese_door_lower", "top": "modernmetals:blocks/manganese_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/manganese_door_item.json b/src/main/resources/assets/modernmetals/models/item/manganese_door_item.json index 9cf555dc..a045787f 100644 --- a/src/main/resources/assets/modernmetals/models/item/manganese_door_item.json +++ b/src/main/resources/assets/modernmetals/models/item/manganese_door_item.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/manganese_door_lower", "top": "modernmetals:blocks/manganese_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/nichrome_door.json b/src/main/resources/assets/modernmetals/models/item/nichrome_door.json index 05152464..86f7819f 100644 --- a/src/main/resources/assets/modernmetals/models/item/nichrome_door.json +++ b/src/main/resources/assets/modernmetals/models/item/nichrome_door.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/nichrome_door_lower", "top": "modernmetals:blocks/nichrome_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/nichrome_door_item.json b/src/main/resources/assets/modernmetals/models/item/nichrome_door_item.json index 05152464..86f7819f 100644 --- a/src/main/resources/assets/modernmetals/models/item/nichrome_door_item.json +++ b/src/main/resources/assets/modernmetals/models/item/nichrome_door_item.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/nichrome_door_lower", "top": "modernmetals:blocks/nichrome_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/osmium_door.json b/src/main/resources/assets/modernmetals/models/item/osmium_door.json index cfceccb3..bed87dbe 100644 --- a/src/main/resources/assets/modernmetals/models/item/osmium_door.json +++ b/src/main/resources/assets/modernmetals/models/item/osmium_door.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/osmium_door_lower", "top": "modernmetals:blocks/osmium_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/osmium_door_item.json b/src/main/resources/assets/modernmetals/models/item/osmium_door_item.json index cfceccb3..bed87dbe 100644 --- a/src/main/resources/assets/modernmetals/models/item/osmium_door_item.json +++ b/src/main/resources/assets/modernmetals/models/item/osmium_door_item.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/osmium_door_lower", "top": "modernmetals:blocks/osmium_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/plutonium_door.json b/src/main/resources/assets/modernmetals/models/item/plutonium_door.json index 83522295..7081ec04 100644 --- a/src/main/resources/assets/modernmetals/models/item/plutonium_door.json +++ b/src/main/resources/assets/modernmetals/models/item/plutonium_door.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/plutonium_door_lower", "top": "modernmetals:blocks/plutonium_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/plutonium_door_item.json b/src/main/resources/assets/modernmetals/models/item/plutonium_door_item.json index 83522295..7081ec04 100644 --- a/src/main/resources/assets/modernmetals/models/item/plutonium_door_item.json +++ b/src/main/resources/assets/modernmetals/models/item/plutonium_door_item.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/plutonium_door_lower", "top": "modernmetals:blocks/plutonium_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/rutile_door.json b/src/main/resources/assets/modernmetals/models/item/rutile_door.json index bba02b7f..03b73c40 100644 --- a/src/main/resources/assets/modernmetals/models/item/rutile_door.json +++ b/src/main/resources/assets/modernmetals/models/item/rutile_door.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/rutile_door_lower", "top": "modernmetals:blocks/rutile_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/rutile_door_item.json b/src/main/resources/assets/modernmetals/models/item/rutile_door_item.json index bba02b7f..03b73c40 100644 --- a/src/main/resources/assets/modernmetals/models/item/rutile_door_item.json +++ b/src/main/resources/assets/modernmetals/models/item/rutile_door_item.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/rutile_door_lower", "top": "modernmetals:blocks/rutile_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/silicon_block.json b/src/main/resources/assets/modernmetals/models/item/silicon_block.json deleted file mode 100644 index 1f0d9839..00000000 --- a/src/main/resources/assets/modernmetals/models/item/silicon_block.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "parent": "modernmetals:block/silicon_block" -} diff --git a/src/main/resources/assets/modernmetals/models/item/silicon_door.json b/src/main/resources/assets/modernmetals/models/item/silicon_door.json index 7097f25d..13ea83fd 100644 --- a/src/main/resources/assets/modernmetals/models/item/silicon_door.json +++ b/src/main/resources/assets/modernmetals/models/item/silicon_door.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/silicon_door_lower", "top": "modernmetals:blocks/silicon_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/silicon_door_item.json b/src/main/resources/assets/modernmetals/models/item/silicon_door_item.json index 7097f25d..13ea83fd 100644 --- a/src/main/resources/assets/modernmetals/models/item/silicon_door_item.json +++ b/src/main/resources/assets/modernmetals/models/item/silicon_door_item.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/silicon_door_lower", "top": "modernmetals:blocks/silicon_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/silicon_ore.json b/src/main/resources/assets/modernmetals/models/item/silicon_ore.json deleted file mode 100644 index 4363ce12..00000000 --- a/src/main/resources/assets/modernmetals/models/item/silicon_ore.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "parent": "modernmetals:block/silicon_ore" -} diff --git a/src/main/resources/assets/modernmetals/models/item/silicon_plate.json b/src/main/resources/assets/modernmetals/models/item/silicon_plate.json deleted file mode 100644 index 1c3a557c..00000000 --- a/src/main/resources/assets/modernmetals/models/item/silicon_plate.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "parent": "modernmetals:block/silicon_plate" -} diff --git a/src/main/resources/assets/modernmetals/models/item/silicon_trapdoor.json b/src/main/resources/assets/modernmetals/models/item/silicon_trapdoor.json deleted file mode 100644 index b8a79f9a..00000000 --- a/src/main/resources/assets/modernmetals/models/item/silicon_trapdoor.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "parent": "modernmetals:block/silicon_trapdoor_bottom" -} diff --git a/src/main/resources/assets/modernmetals/models/item/stainlesssteel_door.json b/src/main/resources/assets/modernmetals/models/item/stainlesssteel_door.json index b562dd1d..95e1c66c 100644 --- a/src/main/resources/assets/modernmetals/models/item/stainlesssteel_door.json +++ b/src/main/resources/assets/modernmetals/models/item/stainlesssteel_door.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/stainlesssteel_door_lower", "top": "modernmetals:blocks/stainlesssteel_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/stainlesssteel_door_item.json b/src/main/resources/assets/modernmetals/models/item/stainlesssteel_door_item.json index b562dd1d..95e1c66c 100644 --- a/src/main/resources/assets/modernmetals/models/item/stainlesssteel_door_item.json +++ b/src/main/resources/assets/modernmetals/models/item/stainlesssteel_door_item.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/stainlesssteel_door_lower", "top": "modernmetals:blocks/stainlesssteel_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/tantalum_door.json b/src/main/resources/assets/modernmetals/models/item/tantalum_door.json index 77fc67c5..c558e79a 100644 --- a/src/main/resources/assets/modernmetals/models/item/tantalum_door.json +++ b/src/main/resources/assets/modernmetals/models/item/tantalum_door.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/tantalum_door_lower", "top": "modernmetals:blocks/tantalum_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/tantalum_door_item.json b/src/main/resources/assets/modernmetals/models/item/tantalum_door_item.json index 77fc67c5..c558e79a 100644 --- a/src/main/resources/assets/modernmetals/models/item/tantalum_door_item.json +++ b/src/main/resources/assets/modernmetals/models/item/tantalum_door_item.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/tantalum_door_lower", "top": "modernmetals:blocks/tantalum_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/thorium_door.json b/src/main/resources/assets/modernmetals/models/item/thorium_door.json index 057c6d8f..8088d428 100644 --- a/src/main/resources/assets/modernmetals/models/item/thorium_door.json +++ b/src/main/resources/assets/modernmetals/models/item/thorium_door.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/thorium_door_lower", "top": "modernmetals:blocks/thorium_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/thorium_door_item.json b/src/main/resources/assets/modernmetals/models/item/thorium_door_item.json index 057c6d8f..8088d428 100644 --- a/src/main/resources/assets/modernmetals/models/item/thorium_door_item.json +++ b/src/main/resources/assets/modernmetals/models/item/thorium_door_item.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/thorium_door_lower", "top": "modernmetals:blocks/thorium_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/titanium_door.json b/src/main/resources/assets/modernmetals/models/item/titanium_door.json index 6321c7e9..e0ee37ae 100644 --- a/src/main/resources/assets/modernmetals/models/item/titanium_door.json +++ b/src/main/resources/assets/modernmetals/models/item/titanium_door.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/titanium_door_lower", "top": "modernmetals:blocks/titanium_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/titanium_door_item.json b/src/main/resources/assets/modernmetals/models/item/titanium_door_item.json index 6321c7e9..e0ee37ae 100644 --- a/src/main/resources/assets/modernmetals/models/item/titanium_door_item.json +++ b/src/main/resources/assets/modernmetals/models/item/titanium_door_item.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/titanium_door_lower", "top": "modernmetals:blocks/titanium_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/tungsten_door.json b/src/main/resources/assets/modernmetals/models/item/tungsten_door.json index 86ee3692..17bf0e85 100644 --- a/src/main/resources/assets/modernmetals/models/item/tungsten_door.json +++ b/src/main/resources/assets/modernmetals/models/item/tungsten_door.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/tungsten_door_lower", "top": "modernmetals:blocks/tungsten_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/tungsten_door_item.json b/src/main/resources/assets/modernmetals/models/item/tungsten_door_item.json index 86ee3692..17bf0e85 100644 --- a/src/main/resources/assets/modernmetals/models/item/tungsten_door_item.json +++ b/src/main/resources/assets/modernmetals/models/item/tungsten_door_item.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/tungsten_door_lower", "top": "modernmetals:blocks/tungsten_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/uranium_door.json b/src/main/resources/assets/modernmetals/models/item/uranium_door.json index 3d8fef1c..63046cc7 100644 --- a/src/main/resources/assets/modernmetals/models/item/uranium_door.json +++ b/src/main/resources/assets/modernmetals/models/item/uranium_door.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/uranium_door_lower", "top": "modernmetals:blocks/uranium_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/uranium_door_item.json b/src/main/resources/assets/modernmetals/models/item/uranium_door_item.json index 3d8fef1c..63046cc7 100644 --- a/src/main/resources/assets/modernmetals/models/item/uranium_door_item.json +++ b/src/main/resources/assets/modernmetals/models/item/uranium_door_item.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/uranium_door_lower", "top": "modernmetals:blocks/uranium_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/zirconium_door.json b/src/main/resources/assets/modernmetals/models/item/zirconium_door.json index c30fa5b8..96a577b6 100644 --- a/src/main/resources/assets/modernmetals/models/item/zirconium_door.json +++ b/src/main/resources/assets/modernmetals/models/item/zirconium_door.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/zirconium_door_lower", "top": "modernmetals:blocks/zirconium_door_upper" diff --git a/src/main/resources/assets/modernmetals/models/item/zirconium_door_item.json b/src/main/resources/assets/modernmetals/models/item/zirconium_door_item.json index c30fa5b8..96a577b6 100644 --- a/src/main/resources/assets/modernmetals/models/item/zirconium_door_item.json +++ b/src/main/resources/assets/modernmetals/models/item/zirconium_door_item.json @@ -1,5 +1,5 @@ { - "parent": "basemetals:item/door_item", + "parent": "modernmetals:item/door_item", "textures": { "bottom": "modernmetals:blocks/zirconium_door_lower", "top": "modernmetals:blocks/zirconium_door_upper" diff --git a/src/main/resources/assets/modernmetals/recipes/nichrome_blend_base.json b/src/main/resources/assets/modernmetals/recipes/nichrome_blend_base.json index 8681ca21..36e6d654 100644 --- a/src/main/resources/assets/modernmetals/recipes/nichrome_blend_base.json +++ b/src/main/resources/assets/modernmetals/recipes/nichrome_blend_base.json @@ -7,7 +7,7 @@ }, { "type": "forge:ore_dict", - "ore": "dustChrome" + "ore": "dustChromium" } ], "conditions": [ @@ -27,7 +27,7 @@ { "type": "mmdlib:enabled", "optionName": "material", - "optionValue": "Chrome" + "optionValue": "Chromium" }, { "type": "mmdlib:enabled", diff --git a/src/main/resources/assets/modernmetals/recipes/stainlesssteel_blend_base.json b/src/main/resources/assets/modernmetals/recipes/stainlesssteel_blend_base.json index 1d841105..a35478e5 100644 --- a/src/main/resources/assets/modernmetals/recipes/stainlesssteel_blend_base.json +++ b/src/main/resources/assets/modernmetals/recipes/stainlesssteel_blend_base.json @@ -7,7 +7,7 @@ }, { "type": "forge:ore_dict", - "ore": "dustChrome" + "ore": "dustChromium" } ], "conditions": [ @@ -27,7 +27,7 @@ { "type": "mmdlib:enabled", "optionName": "material", - "optionValue": "Chrome" + "optionValue": "Chromium" }, { "type": "mmdlib:enabled", diff --git a/src/main/resources/assets/modernmetals/textures/blocks/aluminum_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/aluminum_ore.png deleted file mode 100644 index 12193a9a0129a1c0ec63644f3e66ab94b62c3d54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 470 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>~Q?gt*q%*O!!(^!N8? zWMu5ww`=>3t(&)Q+_H7k?mau#u3xib=eA8-HtgKJWA~mN8#k@nwtdU`jq5gU-thnb z|2N#Csz5^+lf2zsgsS)C0ND(j1s;*b3=G`DAk4@xYmNj^kiEpy*OmP#t1P3i<^5;P zd_bXAPZ!4!iOZ(_jzWhGIGDo^ZIr%zc7o*wroVCvPrtVG?{P^E0Eh1)3%r@WF*E9Zld}Y}% zZSBp=a-5EK>Iuxd-4!+0US0ZorB-t3+0PfN+rQo0>fw4M+v5Xs1uwH14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>~Q?gt*q%*O!!(^!N8? zWMrIM-+pps4nJa0`PlBg3pY5H=O_NT0}jKUTx9&QG@ zfT7jX#W6(UvT47g&|w1(=I}!sr7s`(_}~6E^GZL1q@x@voYpbI3AZQZuyZVuUF$6- z!+hXCd+{RijiH-z*@Z4F>-givXm&kkt@FL9yVC?e{U}$A%X=P%dZD3$!VBleA^aMpiZhlH;S|x4` TmdRZaKn)C@u6{1-oD!M<`(dE1 diff --git a/src/main/resources/assets/modernmetals/textures/blocks/beryllium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/beryllium_ore.png deleted file mode 100644 index 2a81a533ba33fb3c3ab9652434de0690e402af57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 470 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>~Q?gt*q%*O!!(^!N8? zWMmvVcxdmQy*qd8+_iJp!2<_3Z`!Yq3S(3KsEzsfk$L90|U1(2s1Lwnj--eWH0gbb!C6bD$6Lsq`2nK zTcA*@r;B5V#AVZdN1?+89L(W|HcDSU^6|g@ZRVAJ21!RbR5-0;gcEL0%3;u}LZ<+2N1Sl02!i_z?Q&RXYtQ+KBce*ABh77?>1W}9#8>luGNzOrnX zw)W;_IZj7A^#tbK?uwdguP*()QY*Ri?B|Qs?ceTg^>96s?eT%Rf|uDaDto;j&_Svt zt`Q|Ei6yC4$wjF^iowXh$WYh7RM*Hd#K6$X(9+7pK-<8;%D}+G&gcn>hTQy=%(P0} V8Z48$B7hngJYD@<);T3K0RY=EqPzeA diff --git a/src/main/resources/assets/modernmetals/textures/blocks/boron_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/boron_ore.png deleted file mode 100644 index ef64118e7912684c1633953f049ccb226a75b8e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 418 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UASkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#My5g&JNkFq9fFFuY0t>0)3IFPO6{&;}^Ml;rL1!oaYqSJ?>2<1Fxq zEM{Qf76xHPhFNnYfP(BLp1!W^Pg!Ldh0XjU=gkKSP4#qf43W63d&V&9FoOuo#l79! z!pSXMiaeT4`JeeW%ybD^dwIaNSJZstKks+gZcQRT#k8)JWfCds+PD$l%yn< zq*^5xr2;7iBLgEtT?12HBg+s2Ln}i|D-#240|P4q0}nf+Cny?n^HVa@DsgMDOzw&R PYGCkm^>bP0l+XkKp80{T diff --git a/src/main/resources/assets/modernmetals/textures/blocks/cadmium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/cadmium_ore.png deleted file mode 100644 index f75396f1f76042688d72c54ba488c504c8d41c99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 470 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>~Q?gt*q%*O!!(^!N8? zWMu3)bY%P9{hN2}-m+`&?t@3xZQizH?}1G_cJJDMX!n7`8@BD-wtL@(tvfbu-}V3h z|F>^0eghi9nB?v5B2>L62gqjNEbxddW?lFzk+^Ky?w&_?OYM?U_yzs14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>~Q?gt*q%*O!!(^!N8? zWMu5yw}0o(U0b(q+qP}{zJ2>PY}mMK*X}J_w(bFey_+^|*|B5ircIkRZ{G6%|Nrwh zR#^iLVNCLNcM+=IlLKTka29w(7Bet#3xhBt!>lwG!!e;)F^X3DE zT0LDHLnJPn_B#q4HsD|mKeSQ$@{y1K?Qb)$^fO30%Avw(9V47@dr}TN$0FIa-eNM$ z2M)9sFB0Dvx+#}k=)$s&KVFPx*K^i7-+zLk!?d+G zFUxT{+Nmcn?{-(zTzhrt@0D7~rDs21tZx5yZ>xvvk!+6-%oV)MhEdt;{eTWqEpd$~ zNl7e8wMs5Z1yT$~21bUu2Bx}3mLUd)R)&^VCI;FD237_J9(G1gP&DM`r(~v8;?`i9 S+!X=Tz~JfX=d#Wzp$P!ElB6vF diff --git a/src/main/resources/assets/modernmetals/textures/blocks/galvanizedsteel_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/galvanizedsteel_ore.png deleted file mode 100644 index 458b14d8830d19b8774d00dc57ff336074bf8f8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 470 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>~Q?gt*q%*O!!(^!N8? zWMnK`vueSzC36-nn74T0(p4*`&6%}u`O?`7=Py~gY{|+Mv*yoTuypavd2?njn0Ivl zK_ibIkm-y`-tI0!)q8S)YzEE(kH}&M25w;xW@MN(M*=9wUgGKN%Kns9mQmQs?fs=K zK%rJo7sn8Z%clK~LWd1Fn8Ockl)ilAJ?Wf!`ztmBUtquKSGwa)jZ?oJc@_}?lmB4$m@Hs99QGyZyfW!W%o z?aj+_oQ`(t3Cz3Q6*bphUHW^aR&wdt&ljuPzunvF;d&(7;{$UAFSB7(_If{{gH%gg zBT7;dOH!?pi&B9UgOP!ep{{|cu90PkfuWV5rIm?+wt<0_fq{pe(GwI6x%nxXX_dG& USSEKx05vdpy85}Sb4q9e08P@MEC2ui diff --git a/src/main/resources/assets/modernmetals/textures/blocks/iridium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/iridium_ore.png deleted file mode 100644 index ab0f84af191f128c2a9ff705739776a7f0e3fa24..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 470 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>~Q?gt*q%*O!!(^!N8? zWMo`Ex9{A^9jA_NJ$-E3r89ev?q7fYlFzk+^Ky?w&_?OYM?U_yzs14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>~Q?gt*q%*O!!(^!N8? zWMs^qJ$>rbi4!OGPMXv=Yu2>x?)GWZCQX>oGh@b-nKP&M^>s~|GNHG(v%kOl|NsA< zWyK&DF(!Gty9ib9$pNw%I14-?iy0WWg+Z8+Vb&Z8pdfpRr>`sfQ&w3~(;9w3vv{Cx~tB326Y>yAj6}-%bQQ7PLfDTeEag8WR zNi0dVN-jzTQVd20Muxfurn*L!AqIw4hL%<)2HFM&Rt5$hc1BN7H00)|WTsW()?k_3 R6#>-1;OXk;vd$@?2>@NboBaR) diff --git a/src/main/resources/assets/modernmetals/textures/blocks/manganese_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/manganese_ore.png deleted file mode 100644 index c70dc999f716c2bbd072bc3be5c57f034e9f8824..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 470 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>~Q?gt*q%*O!!(^!N8? zWMo|2f8^}0{U^8WKDB-C#eGMPY}|Hs_kj~zcc0&L=)&H^$F}S|y>s8uO*@Wn+4cYb ze>ril#Xv(Clf2zsgsS)C0ND(j1s;*b3=G`DAk4@xYmNj^kiEpy*OmP#t1P3i$-?me zn}I^Do-U3d5|>T;9fb}Xa4?4-+9-Yb$jATox0zS^86+L$P~o(W5l*;0DTke7k?dM; zF&X9q2il7liEj+ul*=x3VOhr?FGjQLIcuHoP2HU)`0>A0T13p6m~FnTuV?)A_{y?j z+S;3!14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>~Q?gt*q%*O!!(^!N8? zWMmv)*>Z4k-M;x%`xaCmUEaK7X33!?^?T=499hIx_fT<(ftSK z^@TSA4Pi|3c6Sk~-jf4lGjJAoL>4nJa0`PlBg3pY5H=O_NT0}jKY@ppEdIV zg<3sb977~7oAx^j9X8-#4nMR}`tp&F|Lt!xukFyd1a56l(3%!X0f>-~TZQY~?f zC`m~yNwrEYN(E93Mg~TPx(24YMwTH4hE|4_Rwf481_o9J1|D`sPf#@E=BH$)RpQoQ TncNit)WG2B>gTe~DWM4fT#lbt diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/aluminum_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/aluminum_ore.png new file mode 100644 index 0000000000000000000000000000000000000000..6cdc2b421855d28652dd7f9fa4ca994460cd4517 GIT binary patch literal 391 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@z%2~Ij105pNB{*tc)B=-So9tZy3N;Q!1MOmhwFP5RQ&YoH4hMY6o0Z~!&Q;H zEROO$7u7Bp@A@|9Fx!Xs32Z9Mm7h(QnX`^L$V+0kaP;b9iaY_DtjgE3*G?By7YvG9 zCnn+cTj;;pq{|x*?)iFM;%K5X?|QvQDqDIF32xvv+uG){>}}8Q=KmYR{q)zCW=|B6 zWZLBLvy#8sd~RQlAzS6d<`*4C)r&QivpXV^?xh`n;CO#4%d(%xemxLno8Yq4B1ou3oE;zjfht{rE`!$3Zd6=i3_np3hn4a6fHwW%B;e hfT`~1t-0fSwSDs^H@wYTV*&I#gQu&X%Q~loCIHrcmN5VT literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/aluminumbrass_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/aluminumbrass_ore.png new file mode 100644 index 0000000000000000000000000000000000000000..6d2804307d395276b383395de26b40a7850f90b4 GIT binary patch literal 418 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@z%2~Ij105pNB|Az_H=O!vFJS-6zF%zL16COZ8DsTeD|~(CLJ>TA#|ndo7BGs zMW-s(Iswx|nM+Jd%8#FUwC-r`4+FROO?PjeIa8g#i~GZU0keoy&8f3_U3%H}h+E2R zTOYfw>-k5~%(V|5n5;Z2t+P4$QEl1vrPJGw9I5XPomO?~^FzzMer)rk*T_w~EPY*~ zP0*&V%9_`8n3s!k8kWKEqch3vpG;Se%C_Y8;gQY*(}nUb-ebx>UG6OdX9fi=AC?V)9RAf&g-5_ z98bNi|9nIId#zB%y2gC<`2CDY4DNHUtS;wU^{wIhJi$6q`NAucxGdCf9R!9IgQu&X J%Q~loCICOOqTB!g literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/beryllium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/beryllium_ore.png new file mode 100644 index 0000000000000000000000000000000000000000..a7accaa8bc63517dc1e0052ca16fb5e5494b43df GIT binary patch literal 387 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@z%2~Ij105pNB{-jc)B=-So9tZI^EZ7z*7?RV`GqL)Yt6o?W|&_9?#F<%XxIw z#gtq1)xuS`CN{}hZcE?LX~(PTcmJzRpYqW{pJNhUn@hUR28NqPXJ^iT*Id}Y?ybbP zKc&ySYd`+0zF7WuRq?;M#g|RA&8q4$XP0^PEb8dm6QMF=HD~#nx>d`!X4T9oKByq! z$?Vavw|=o)=y zvOB5M^-`2RcgprxK|b#ead>W;yCAhHqMmbP=; ch2pyGlLV)QoptP=3G_FEr>mdKI;Vst0PtFuE&u=k literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/boron_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/boron_ore.png new file mode 100644 index 0000000000000000000000000000000000000000..e6665f3f0658d79c639ceb7d140eef1047112bc4 GIT binary patch literal 331 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@z%2~Ij105pNB{-5c)B=-SoEF^+RJrBfv3-}mcRXy(xZSS4-{(uul*qvJC}vO zxh&vyWr581Mz^!mcp%}1j!4$5bQ|`8hB$`={Ma=(EoUwLwk4IrY z%cNI~*Gmsp&RcRNeBSol-P}JkGw$s9zBcF7nW6>n#8*uV^g3wCe%WNtA)%pE$2RaC%e1hGnTzmnR&ry#M`8!VYf_InU496^tAG WT(wm^7m5Mh&*16m=d#Wzp$P!inRrwH literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/cadmium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/cadmium_ore.png new file mode 100644 index 0000000000000000000000000000000000000000..b12af2d760cbc1673ed146e455149a609f1165a0 GIT binary patch literal 405 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@z%2~Ij105pNB{-@dAc};So9tZdh2z>Kw!?3TgQd`yqDI=+O4#=u{{0t)!eP; zr-&8Jo~pYu-qpZ`>U>)nn#|KPvdElO%H{oI*XQU6BB+$zRodH>8APbIgN3Jabw&?&!Tns0w@ z*2&|B$HSQ1pZ;?4Y&I6$bd_uS)6kONue3`gXErtUgeyf>A6uq1)kS!B(CeiZ`}S$- zJ!hL|Q+m~^??L`e!9*GNmK9y4k0y02nA*i^cJQ>Y;r?%ZEl=XrjhpApKBx9d|MxW4 xyTU0}$0Rb=eyj*reV)#~(Ds+wq4kf1--k?SpThU#BQPWwJYD@<);T3K0RW*pq0ayS literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/chromium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/chromium_ore.png new file mode 100644 index 0000000000000000000000000000000000000000..5b5db26404baf523052d08ec0a34b748623896ab GIT binary patch literal 384 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@z%2~Ij105pNB{+2db&7ASP-x#qt$k=1trs4+lG=rzBpUXO@geCxCDwVMS literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/galvanizedsteel_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/galvanizedsteel_ore.png new file mode 100644 index 0000000000000000000000000000000000000000..afbbc42b4b8976b22c627bebdd2533e33c5c6143 GIT binary patch literal 398 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@z%2~Ij105pNB{-Dd%8G=So9tZO3ZIE5ZHUK;s1*g=iapc8S+_2rmNja0Ie2ld{=W0{*NGyMnM)IXidBDR zs|*!7e*SLTrR2OH{})+=D?iQMwS0|^Ti%BKCr(Y)<~b}-cK_r8hg040h9~*=&lCN* zeCs#0n-fJJ2wMERqbyhYTwB=5;r2EGmd4K46^a!mN=7oZk0b2r=UOsG=j3$n&P(8o ze!{M#f4I3z{qjWbr6L?h5}UuS-JP3u{aWDiDb;gx;>+j$=h?@nsklh{!R?i=rQ!r0 oChwGbTh90E+mC9KL-QYTzopr0P_c&r2qf` literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/iridium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/iridium_ore.png new file mode 100644 index 0000000000000000000000000000000000000000..f33bce703172b39347821c4d2fd11b96c27188bf GIT binary patch literal 408 zcmV;J0cZY+P)z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy32;bRa{vGf6951U69E94oEQKA0Rc%wK~y-6#Zt*m!!QsXPZRzJ zBvP@6D+rMKsazzdgbE>vev=j@nv-1*J}1mJda*(Xg+@Q{omF)AGVYyueY?AKDFFneX*Pk z0x(Han2h=jK&f?H@#Fpe$61jF=i*p)Bmx|P=kI6KE_Qx01qv!-AW1r4Dg)W+wer!3 ze2=j>(jRm$f`EX%s@m!%(y&GAER?!z5bk%+TP~fopb(n`*%!Nz$yyxo?~~t2MBuE6 z-@)|lju2G<@aq&s0oXw}I_tI02M2X~&hC!ztMv&n+?B7Vw~jyn0000eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@z%2~Ij105pNB{-jdb&7b`tKzgr z{K+}&CYrwIEHk-3jQ-{8MD}G?B{%+c4Cvn@j{mKx23*|=53eJ zlORn^zAA7PEY&KN#UEH5O^~>yKCwF-E zMKriA_z|UiZ{y{uoQezHS8veqKB{noQ;JtGRL}Qu!Mk1mbPk;L3biTK^*f% e`@0@xs literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/manganese_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/manganese_ore.png new file mode 100644 index 0000000000000000000000000000000000000000..c15b0575a5996fc66a18895f21507d05db27fa6d GIT binary patch literal 414 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@z%2~Ij105pNB|9I_jGX#vFJS-blZ#BP++d-^8dWdz-=!lS9P04bZc5bQgRVmFlGeO8 zPF$Dxzvfcq`v9Gyjn#FwlS_ZSc(mVj;#1wHSADhe`Tcdxwe0qspWf)<^1;SinkDvC z-qGv3kFL*F-R+@kzo*9f)@+%Dq6vpDESBT(Ki>Q9@wPW*DGeNXdt3rS+1#dc-n!&+ z;OMH=GV=V()Ey5@o}1;ec*p58Jo2eGJuE!>qV?WdFWoRR;mg#!>Yvl!uSnzDAM9{o z#;)D$`rC^gTN@LWFE*D>|NY_Bny+>i9d8-)^%Ix>V0pTAqJctmCMYHtJYD@<);T3K F0RXElpYi|z literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/nichrome_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/nichrome_ore.png new file mode 100644 index 0000000000000000000000000000000000000000..726c1eace40c2a3e7f3845d1a676b36963af1880 GIT binary patch literal 421 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@z%2~Ij105pNB|Az^K@|xvFJS-6zj!oC@}BLKbh4|-181KiS23RUmCP@>(nOo z53a69FG>GMTAB2+bC>pRPq(zvN9&}21$5Q%pA=X7eAfIV!yof06RzgCL{4**IX*#+ z)1+T7c1_Rqc4MPs4xbC&^omFL>34j8{#v-I+D8A!{UlTAt6KZ-l)U_#8BpB(^l*_? zNoSzQ!9Ux=Js)iUm{fTG{;u#RN26ZzZc^4fp!?z9@4j10&c!dA!Zi1}K#SrFo93|e zMLSHDq6@xkRjyfYTf(aHbM+hly8#^0PZ)oCT<%JEoDfi)+0(|*yvo7*%KoEIbozAs z=liHLX3YJ4KcTMNtK)^xkMc=hr36IZ$j4uOXU(2keJm$0arqAxJNtem-^f>wfWgJ! M>FVdQ&MBb@0QwiFdH?_b literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/osmium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/osmium_ore.png new file mode 100644 index 0000000000000000000000000000000000000000..cf803c4a09fd23ee5a380de21b16bae9727ccb26 GIT binary patch literal 407 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@z%2~Ij105pNB|9I@N{tuvFJS-li=@j{A-c_-=)_EIt&RD;FpJ88eh0pzH-l*3b z9_4KOR~q(Y;i^M>-*~WCFr9BtzhYQcXS7sAB3ZGkDxs(GWx)aqsld?d5?#t)-#$3Q z?NPjTRb7d)<_)F#35$N@2=?jymYses(&5ck&Sm%CIH_e99(nS~x|3ne)`!*=r@~rR y)G+TqSGS%~@L^ubRm=CXCT}gvZ4S+U#9erz-$wlOlYPJdV(@hJb6Mw<&;$UJ`Jg)h literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/plutonium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/plutonium_ore.png new file mode 100644 index 0000000000000000000000000000000000000000..ab038d659f127e8eb37b4e293b7621ce265db371 GIT binary patch literal 419 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@z%2~Ij105pNB|Az@pN$vvFJS-bk>X6P+;DY-L=cQmj2-5(yREg$Vqgo{vvi! zn{PpZnQxgZG(x-IKEAQzOlW_Un3(Xzp6lYGTsrO{~w|k1o<9kh;blTI5**5zq z?#%hUx9G0yK7m_I+w`wB|6V8O_xwn4{r8FQ`qS3^zb*A$%=GEOdo69p*PEK}+9NUd zjIBYRvcwyIZHCP?{Mz@*=GV^&{Tz@Wak|f?LHWa{zgMosi1>H%@BVg-7=L z*QpCKMoI5$p53f|iT5evAFF3)EsoB8-FvI@{em3+!qS%bwfwTm6GR?gDLfAhECx?k KKbLh*2~7ZXRHXm_ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/rutile_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/rutile_ore.png new file mode 100644 index 0000000000000000000000000000000000000000..72639290f39df587f21a36a3ec553462b679d71f GIT binary patch literal 411 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@z%2~Ij105pNB|9I@pN$vvFJS-blb1RKw$2*P4>$>h4vrt{owRy|Br+{4q{<_H(c;7;)mK~D(w{j*PAMp=yZZFk zk1i*ZV+S^tXP!0ph)-Jb_3Jmb{f{?vmUrt!_G#_6z4!3^<)kNW-^^bYUlE-8_&{}m zy~2gN5g&JQYrz4n18%d}Q*3y_&`^p-@#)T0g`KD-HUag_J;vJ@$ZE){!hYpo>o-)~d?s#A6B z@(-COSqZ-=?XqFlgTe~DWM4f DPLiX% literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/stainlesssteel_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/stainlesssteel_ore.png new file mode 100644 index 0000000000000000000000000000000000000000..7683080dc592c32e5f738089c3e1a736867e9458 GIT binary patch literal 394 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@z%2~Ij105pNB{*td%8G=So9tZdYi|jC=fH@^TR*uuC-rTcFlTfLe08Iu6;`* zZygQR`)A;5ee&R;P1eQ*4F7o#9y~dtV5h~4@7x=KH~+w*%<)veV_7zM2- zFX`Ys!F2X}`jN%&zN9#D6s%)hT6MvK{qoTmmu$vlo!xH=Z07U`Gm5)k6I=VJLB*`2 zVBPE4ZOtDwf?o m*Wc6xU6|&6UzvMeK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@z%2~Ij105pNB{-jd%8G=So9tZddt^jz@sBvV<{Tgb3VgF>yzSxYE8FoS4DK2 zbp9B)={J_XTvz^P<&IxoaYIu2Pb+PY}#FnV6g6F}>`<&{|Ot^M7 ztJiZz$jAIbug`r&8}24c$y?{S6i-&o^evH*=G(o%b-_&uLH3Ks_j3KpT-)nebcE>& z)7tmxYxcb=NfqOmu&JfdmLbyX!=IV1YZa43xr41``tN$`9(dCJ%KQ4W1|6-X3wCT{ zH!X6Po$jSA;IxIi;{2;rH!>V&EyXKyL8l(B^wCnSlw+k}+ f_w5tg*JU01Xv&w6M_2v;z0Tn2>gTe~DWM4flwp?B literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/titanium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/titanium_ore.png new file mode 100644 index 0000000000000000000000000000000000000000..5ed04a22870a86dcf9c398f270dc8d1351d052d9 GIT binary patch literal 398 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@z%2~Ij105pNB{-Dd%8G=So9tZ`kU8eAh18|L27IW&*dKs-JQltTt>D2UQU_$ zmYZHXNd1ox+pG5F)Ym%sT*f%rrpb%;BwjWVa1~q;b?|r5!>qp^n`L@UCmbox-`oAC z$aR+eHfx)CU3&F-B2ROV?76+X;?(JhQa9u0bn9Q2Xj80_>uY9PI%WRH33LCy3B7bY z>b349B{cmdKI;Vst0NFj65&!@I literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/tungsten_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/tungsten_ore.png new file mode 100644 index 0000000000000000000000000000000000000000..cbe271a973bb700ce82509a24a373693871459b6 GIT binary patch literal 381 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@z%2~Ij105pNB{+&dAc};So9tZddt;pz~j^XYZjN*50`&|fq}oii!-I}e3yM>QT8sI=Wv6_XPe{O za!db3Yb@1yx=3TgYpc_}Hu}HznfLB{?;JU8I-8ZW`m0_4D#ASXKem`N`$-3D220m_ z(?@-C*ZFWTzTVaJywYk}rdEcD%axray@xABT-_RGZ(UXKvTDu**D8h2Ht`&X4YtGt z&7JbeYl7X)nLXFvbj@fJYFHXS!eUB-sa7=zuvk1(31Of XZxS=_wd4;#A2WEm`njxgN@xNA-piGe literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/uranium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/ore_overlays/uranium_ore.png new file mode 100644 index 0000000000000000000000000000000000000000..7fe6c81a9f4636fd9721508c59991dd187901506 GIT binary patch literal 412 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@z%2~Ij105pNB|9I^>lFzvFJS-bUTa5P+)FK`P=WBTg28r5dRUxqPcCK=R%QR z>~>ootzCEH?^dbYWTVt3;j-Na+k?Iwc;_?YjAi;HhB?wrlTD-_Z2#BfRm7OJ`oXhT zC$E2+vCP)wn8RnInDU4;evd+vcMoz(pMScnbe2u^>Ghksde{C=lAamAr~c0GV=8_R za_{Mytaz)o#v=0DKbzG{=5JMtP;u9BP!9OB*Ig!gxwf!V(>19_7DXFF>%~l@7M1Sg z={@|j^zB9Ng3ZBaPac~y!L>?Z_gpQ3YpV}W^3LYrkQC*NVlPvBY-+7kR5w>lpkV#s zbc48(z*8s8FTBeK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@z%2~Ij105pNB{+Ydb&7@Zk~qz=Xw8iR~6d^-jgj0nbXxNEhBeo6;G$}+yl|u_8n@^B zMFndi#=OSw>&vf{U#(2>Vw6aB%;r5-GC?o+=vu*_^OD!dKJ4fJte|jGewBHwZi}wf z>xFy!14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>~Q?gt*q%*O!!(^!N8? zWMs@-w0!cMh5a+;O_(`<#=>QtlV(hryQpvaoN4oyPG7LJXX@-pvlnzvnbkXO_W%F? z?NMQuIx`)Wf_Gn?>}qi z0}8czx;TbNTsG}@6gq6c!5n^Qqx9t?AOG9mW?t!MkaUzoh0{7lIN|oB9CnUHvTMD? zWS9>eXfIwQzAeAmUwUSHEe!f`U{_WmY57#5v9v_%1c$p2Ove)|o9i&>~ z8c~vxSdwa$T$Bo=7>o>z40R1mb&V`T3=FLdEv-xpv<(cb3=BN%jGmxq$jwj5OsmAL U!7{lk0;qw()78&qol`;+001hVA^-pY diff --git a/src/main/resources/assets/modernmetals/textures/blocks/plutonium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/plutonium_ore.png deleted file mode 100644 index e635e2b4d122fd335ceb4cb54db5e91e51933a1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 470 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>~Q?gt*q%*O!!(^!N8? zWMphAIkP73_=?OUE3=MnEIz$BdEeUn6Du+ftuH*aq3HC|w1ca2k1b6-uq^%1|Ns9t z$0t1l8p4?5?d~E}y(b6AX5cLFh%9Dc;1&j9Muu5)B!GhKC7!;n>`z%`8HG)+1)Ns} z3blH=IEF}EHtly5I&8qf9DZn{^yMQT|J&bYUg>9$bd*Dd(>g{t;r65)c8*1|YrVx} zm=7FiFJ2_RF?3TdyU>MY9e=zS&93LHb-p)scbeeG|5j-cF>7MB`L@2E@z>)k%Z6!d zZ(f$;bhJ}XVBYPnsJZs)(%&n!l1tBizF6JcptHiCr TGPx@PsDZ)L)z4*}Q$iB}O){Vt diff --git a/src/main/resources/assets/modernmetals/textures/blocks/rutile_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/rutile_ore.png deleted file mode 100644 index 77a7089903353d83043d6ebacc160acf7fd71d64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 470 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>~Q?gt*q%*O!!(^!N8? zWMu4IvS7=+*&Aj}-#B~5j>Yp=PnocF{_OQLr)^s}cgLc6Yo|}yJa^XGX_ME@nELc7o*wroVCvPrtVG?{P^E0Eh1)3%r@WF*E9Zld}Y}% zZSBp=a-5EK>Iuxd-4!+0US0ZorB-t3+0PfN+rQo0>fw4M+v5Xs1uwH14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>~Q?gt*q%*O!!(^!N8? zWMu5#v199|%^TNk*tCAb&TTtZuUxfl^OlWk*X`K4ZRfUa>sPPYvT@Ve)oa$TU3YZ< zL3aDc6+lB6lf2zsgsS)C0ND(j1s;*b3=G`DAk4@xYmNj^kiEpy*OmP#t1P3ixkcPM zPM}b$r;B5V#AVZdN1?+89L(W|HcDSU^6|g@ZRVAJ21!RbR5-0;gcEL0%3;u}LZ<+2N1Sl02!i_z?Q&RXYtQ+KBce*ABh77?>1W}9#8>luGNzOrnX zw)W;_IZj7A^#tbK?uwdguP*()QY*Ri?B|Qs?ceTg^>96s?eT%Rf|uDaDto;j&_Svt zt`Q|Ei6yC4$wjF^iowXh$WYh7RM*Hd#K6$X(9+7pK-<8;%D}+G&gcn>hTQy=%(P0} V8Z48$B7hngJYD@<);T3K0RY~Vp#A^= diff --git a/src/main/resources/assets/modernmetals/textures/blocks/tantalum_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/tantalum_ore.png deleted file mode 100644 index 060dd1c5f703a10189892413f65880780c37890a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 470 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>~Q?gt*q%*O!!(^!N8? zWMu5xzH`f_%^TNk*tBlr&TTtZuUNHp)7A}Z*KgmlZO7K_>sGJbvSIVuRcqF-S@-|{ z|I9_EAAp81CV9KN2vzUN0kRo53p^r=85p>QL70(Y)*K0-AbW|YuPgggR#`@2%lpro z`G7*Lo-U3d5|>T;9fb}Xa4?4-+9-Yb$jATox0zS^86+L$P~o(W5l*;0DTke7k?dM; zF&X9q2il7liEj+ul*=x3VOhr?FGjQLIcuHoP2HU)`0>A0T13p6m~FnTuV?)A_{y?j z+S;3!14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>~Q?gt*q%*O!!(^!N8? zWMoX8KC6G?lJwR&O%+~g<36rLF_4H4gGJW#Y8691{eG@0QclLC4_x=C>KO^Nr z7SIsJByV>Yq3S(3KsEzsfk$L90|U1(2s1Lwnj--eWH0gbb!C6bD$6KrdM)6*Dp07^ z)5S4F;<9PKqtIak4(9Mf8>KHF`S{=dHuFk9gQTMzDxB6a!U?x0<*;)sl3nX9Cc}K- zKzs2b@r|LIa@mD0EbI8=#b|atXRY(Ssk_qzKmNB$i-=hhv(2~l^^CtBUs*OxTYK}e z9H*n5dIIxqcSX&$SC{@?sg+!M_VdN+_HXyLdbl3R_V~bD!OLtImA&2%=pfY+*NBpo z#FA92R`LvDUbW?Cg~4VKAW Q5kL(Lp00i_>zopr0I+7APyhe` diff --git a/src/main/resources/assets/modernmetals/textures/blocks/tungsten_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/tungsten_ore.png deleted file mode 100644 index 78a89a97e657c5e6423215883ae5da201b20a65a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 414 zcmeAS@N?(olHy`uVBq!ia0vp^0wBx*Bp9q_EZ7UASkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#My5g&JNkFq9fFFuY0t>0)3IFPO6{&;}^Ml;rL1!tlSn|IB_MkF&rd zvY3H^TNs2H8D`Cq01C2~c>21sKV_9=6t;4Ee`yO)XriZ!V~E6M-7^~t4;zTET#SFG z|KO8uqH(WF19OtjWN&Aa7!CSQ% z-^o_}QSsHhUwdEpnETtLx9%mA917o=)kG$*yjDCf`+Rl^AJ>^nZ!Evagc#q`cW6xh z=IplVnvY#R*Vh>1-(UGM7%lsh7pR0x+QOvLXzwP`^09x{r3*mEsg}4#l%ynbP0l+XkK6DEb5 diff --git a/src/main/resources/assets/modernmetals/textures/blocks/uranium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/uranium_ore.png deleted file mode 100644 index 342ea9ce7ec00396468829512a6c35367c7f78f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 470 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>~Q?gt*q%*O!!(^!N8? zWMr({T)$#n#gbK}OIMe!-CQ?+Y2M29Rf|^^uijX*W>f9L6@|;!RxDUnuy{q$|NsBH zcOB6J8p4?5?d~E}y(b6AX5cLFh%9Dc;1&j9Muu5)B!GhKC7!;n>`z%`8HFwHKWpX# z3blH=IEF}EHtly5I&8qf9DZn{^yMQT|J&bYUg>9$bd*Dd(>g{t;r65)c8*1|YrVx} zm=7FiFJ2_RF?3TdyU>MY9e=zS&93LHb-p)scbeeG|5j-cF>7MB`L@2E@z>)k%Z6!d zZ(f$;bhJ}XVBYPnsJZs)(%&n!l1tBizF6JcptHiCr TGPx@PsDZ)L)z4*}Q$iB}6JVb( diff --git a/src/main/resources/assets/modernmetals/textures/blocks/zirconium_ore.png b/src/main/resources/assets/modernmetals/textures/blocks/zirconium_ore.png deleted file mode 100644 index a56eb87f841afcda945498da2597cff7a5f784c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 470 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>~Q?gt*q%*O!!(^!N8? zWMnK_vUtw?xie~(;9w3vv{Cx~tB326Y>yAj6}-%bQQ7PLfDTeE zag8WRNi0dVN-jzTQVd20Muxfurn*L!AqIw4hL%<)2HFM&Rt5$hc1BN7H00)|WTsW( V)?k_36#>-1;OXk;vd$@?2>|wppX2}l diff --git a/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta index b0f2ba20..6361a04d 100644 --- a/src/main/resources/pack.mcmeta +++ b/src/main/resources/pack.mcmeta @@ -1,6 +1,6 @@ { "pack":{ - "pack_format":2, + "pack_format":3, "description":"Modern Metals" } -} \ No newline at end of file +} diff --git a/tools/validation/legacy112/audit_resources.py b/tools/validation/legacy112/audit_resources.py new file mode 100644 index 00000000..6904171f --- /dev/null +++ b/tools/validation/legacy112/audit_resources.py @@ -0,0 +1,30 @@ +"""Parse the JSON resources shipped to Minecraft.""" +import json +import sys +from pathlib import Path + +root = Path(sys.argv[1]) +files = sorted(set(root.rglob("*.json")) | set(root.rglob("*.mcmeta")) | {root / "mcmod.info"}) +for file in files: + try: + json.loads(file.read_text(encoding="utf-8-sig")) + except (OSError, ValueError) as exc: + raise SystemExit(f"{file}: {exc}") from exc + +assets = root / "assets/modernmetals" +assert json.loads((root / "pack.mcmeta").read_text(encoding="utf-8"))["pack"]["pack_format"] == 3 +for file in sorted((assets / "models").rglob("*.json")): + model = json.loads(file.read_text(encoding="utf-8")) + parent = model.get("parent", "") + assert not parent.startswith("basemetals:"), f"{file}: requires the optional Base Metals mod" + if parent.startswith("modernmetals:"): + model_path = parent.split(":", 1)[1] + assert (assets / "models" / (model_path + ".json")).is_file(), (file, parent) + +for locale in ("de_de", "en_us", "ru_ru", "zh_cn"): + lang = (assets / "lang" / (locale + ".lang")).read_text(encoding="utf-8-sig") + keys = {line.split("=", 1)[0] for line in lang.splitlines() if "=" in line and not line.startswith("#")} + assert {"config.modernmetals.title", "config.modernmetals.inherited_mode", + "config.modernmetals.content_mode", "config.modernmetals.content_mode.high_fantasy", + "config.modernmetals.content_mode.low_fantasy"} <= keys, locale +print(f"Verified {len(files)} packaged JSON resources") diff --git a/src/main/resources/assets/modernmetals/orespawn/modernmetals.json b/tools/validation/legacy112/fixtures/orespawn3-modernmetals.json similarity index 99% rename from src/main/resources/assets/modernmetals/orespawn/modernmetals.json rename to tools/validation/legacy112/fixtures/orespawn3-modernmetals.json index 3dfbb82a..1a5d8712 100644 --- a/src/main/resources/assets/modernmetals/orespawn/modernmetals.json +++ b/tools/validation/legacy112/fixtures/orespawn3-modernmetals.json @@ -370,4 +370,4 @@ ] } } -} \ No newline at end of file +} diff --git a/tools/validation/legacy112/run_packaged_suite.py b/tools/validation/legacy112/run_packaged_suite.py new file mode 100644 index 00000000..88ded9fa --- /dev/null +++ b/tools/validation/legacy112/run_packaged_suite.py @@ -0,0 +1,161 @@ +"""Run the packaged mod on disposable Minecraft 1.12.2 servers.""" + +import argparse +import hashlib +import json +from pathlib import Path +import shutil +import subprocess +from zipfile import ZipFile + +from jsonschema.validators import validator_for +from referencing import Registry, Resource + + +def dependency(spec, mirror): + group, name, version = spec["coordinate"].split(":") + jar = mirror / group.replace(".", "/") / name / version / f"{name}-{version}.jar" + assert digest(jar).upper() == spec["sha256"], jar + return jar + + +def digest(path): + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def write_config(directory, mod, mode, boron_disabled=False): + text = f"general {{\n S:contentMode={mode}_fantasy\n}}\n" + if boron_disabled: + text += "metals {\n B:EnableBoron=false\n}\n" + (directory / f"config/{mod}.cfg").write_text(text, encoding="utf-8") + + +def run(java, directory, phase, flags): + report = directory / "modernmetals-review.json" + assert not report.exists(), report + console = directory / f"{phase}-console.log" + with console.open("w", encoding="utf-8") as log: + process = subprocess.run( + [str(java), "-Xmx1200M", "-Dfml.queryResult=confirm", *flags, "-jar", + "forge-1.12.2-14.23.5.2859-universal.jar", "nogui"], + cwd=directory, stdin=subprocess.DEVNULL, stdout=log, stderr=subprocess.STDOUT, + timeout=150, check=False, + ) + assert process.returncode == 0 and report.is_file(), console + report.rename(directory / f"{phase}-review.json") + return json.loads((directory / f"{phase}-review.json").read_text(encoding="utf-8")) + + +def prepare(template, target, installed): + shutil.copytree(template / "libraries", target / "libraries") + for filename in ("forge-1.12.2-14.23.5.2859-universal.jar", "minecraft_server.1.12.2.jar"): + shutil.copy2(template / filename, target) + (target / "eula.txt").write_text("eula=true\n", encoding="utf-8") + (target / "server.properties").write_text( + "online-mode=false\nserver-ip=127.0.0.1\nserver-port=0\nmax-tick-time=-1\n" + "level-seed=6713525\nview-distance=3\n", encoding="utf-8", + ) + for folder in ("config", "mods"): + (target / folder).mkdir() + for jar in installed: + shutil.copy2(jar, target / "mods") + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + for arg in ("java", "template", "output", "mirror"): + parser.add_argument(f"--{arg}", type=Path, required=True) + args = parser.parse_args() + args.output.mkdir(parents=True) + specs = json.loads(Path("gradle/dependencies.json").read_text(encoding="utf-8")) + jars = {spec["id"]: dependency(spec, args.mirror) for spec in specs} + mod = Path("build/libs/ModernMetals-2.5.1.112021.jar") + probe = Path("build/test-mods/modernmetals-probe-packaged.jar") + with ZipFile(jars["orespawn"]) as archive: + schemas = [json.loads(archive.read(name)) for name in archive.namelist() + if name.startswith("META-INF/orespawn/docs/schemas/") and name.endswith(".json")] + registry = Registry().with_resources((schema["$id"], Resource.from_contents(schema)) for schema in schemas) + schema = next(schema for schema in schemas if schema["$id"].endswith("/orespawn-provider.schema.json")) + validator = validator_for(schema)(schema, registry=registry) + + fresh_provider = None + for name, local_low, inherited, boron_disabled, integrations in [ + ("standalone-high", False, False, False, ()), + ("standalone-low", True, False, True, ()), + ("inherit-base-low", False, True, False, ()), + ("tinkers-high", False, False, False, ("mantle", "tconstruct")), + ("tinkers-low", True, False, False, ("mantle", "tconstruct")), + ("titanium-opt-in", False, False, False, ()), + ("os3-import", False, False, False, ()), + ]: + target = args.output / name + installed = [jars["mmdlib"], jars["orespawn"], mod, probe] + installed.extend(jars[integration] for integration in integrations) + if inherited: + installed.append(jars["basemetals"]) + prepare(args.template, target, installed) + if inherited: + write_config(target, "BaseMetals", "low") + write_config(target, "ModernMetals", "low" if local_low else "high", boron_disabled) + + rule_id = "modernmetals:legacy/titanium_ore" + if name == "titanium-opt-in": + fresh_provider["ores"][rule_id]["enabled"] = True + override = target / "config/modernmetals-orespawn.json" + override.write_text(json.dumps(fresh_provider), encoding="utf-8") + source_path = override + elif name == "os3-import": + legacy = target / "config/orespawn3/modernmetals.json" + legacy.parent.mkdir() + original = Path("tools/validation/legacy112/fixtures/orespawn3-modernmetals.json") + settings = json.loads(original.read_text(encoding="utf-8")) + settings["spawns"]["aluminum_ore"]["parameters"].update( + size=5, variation=0, frequency=2.75, minHeight=10, maxHeight=50) + settings["spawns"]["thorium_ore"]["enabled"] = False + legacy.write_text(json.dumps(settings), encoding="utf-8") + source_path = legacy + else: + source_path = None + original = digest(source_path) if source_path else None + + flags = [f"-Dreview.low={str(local_low or inherited).lower()}", + f"-Dreview.inherited={str(inherited).lower()}", + f"-Dreview.boronDisabled={str(boron_disabled).lower()}", + f"-Dreview.titanium={str(name == 'titanium-opt-in').lower()}"] + first = run(args.java, target, "first", flags) + provider = first["provider"] + if name == "standalone-high": + fresh_provider = provider + validator.validate(provider) + rules = provider["ores"] + assert len(rules) == (16 if boron_disabled else 17), rules.keys() + assert not rules[rule_id]["enabled"] + assert all(not rule["retrogen"] for rule in rules.values()) + profile = first["profile"] + assert not profile.get("terrain_dimensions") + assert profile["ores"].get(rule_id, {}).get("enabled", False) == (name == "titanium-opt-in") + selector = "orespawn:all_except_nether_end" + if name == "titanium-opt-in": + assert (profile["ores"][rule_id]["dimension_selectors"][selector] + == profile["ores"]["modernmetals:legacy/rutile_ore"]["dimension_selectors"][selector]) + if name == "os3-import": + aluminum = profile["ores"]["modernmetals:legacy/aluminum_ore"]["dimension_selectors"][selector] + assert (aluminum["frequency"], aluminum["min_y"], aluminum["max_y"]) == (2.75, 10, 49), aluminum + assert not profile["ores"]["modernmetals:legacy/thorium_ore"]["enabled"] + group = profile["ore_material_groups"]["orespawn:aluminum"] + assert {"oreAluminum", "oreAluminium"} <= set(group["ore_dictionary_entries"]) + if name == "os3-import": + # The 1.12 importer keeps the ambiguous legacy Bauxite alias for review. + assert any(key.startswith("orespawn:review/") and "oreBauxite" in item["ore_dictionary_entries"] + for key, item in profile["ore_material_groups"].items()) + else: + assert "oreBauxite" in group["ore_dictionary_entries"] + assert run(args.java, target, "reload", flags) == first, name + if source_path: + assert digest(source_path) == original, source_path + print(f"{name} passed fresh world and reload", flush=True) + print(f"Packaged game SHA-256 {digest(mod)}") + + +if __name__ == "__main__": + main() diff --git a/tools/validation/legacy112/runtime-probe/review/modern/ClientChecks.java b/tools/validation/legacy112/runtime-probe/review/modern/ClientChecks.java new file mode 100644 index 00000000..b567e0a9 --- /dev/null +++ b/tools/validation/legacy112/runtime-probe/review/modern/ClientChecks.java @@ -0,0 +1,97 @@ +package review.modern; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Collections; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiMainMenu; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.resources.I18n; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.client.FMLClientHandler; +import net.minecraftforge.fml.client.IModGuiFactory; +import net.minecraftforge.fml.client.config.GuiConfig; +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent; +import net.minecraftforge.fml.common.registry.ForgeRegistries; +import zone.moddev.mc.modernmetals.content.ContentPolicy; + +public final class ClientChecks { + public static void install() { + MinecraftForge.EVENT_BUS.register(new ClientChecks()); + } + + private GuiScreen menu; + private GuiConfig screen; + private int phase; + + @SubscribeEvent + public void check(final TickEvent.ClientTickEvent event) throws Exception { + if (event.phase != TickEvent.Phase.END || phase == 3) return; + final Minecraft minecraft = Minecraft.getMinecraft(); + if (phase == 0 && minecraft.currentScreen instanceof GuiMainMenu) { + menu = minecraft.currentScreen; + final IModGuiFactory factory = FMLClientHandler.instance() + .getGuiFactoryFor(Loader.instance().getIndexedModList().get("modernmetals")); + require(factory != null && factory.hasConfigGui(), "Forge's Mods factory is missing"); + screen = (GuiConfig) factory.createConfigGui(menu); + minecraft.displayGuiScreen(screen); + phase++; + } else if (phase == 1) { + verifyScreen(screen, menu); + final Class directoryType = Class.forName("zone.moddev.mc.orespawn.client.OreSpawnModsScreen"); + final Constructor constructor = directoryType.getDeclaredConstructor(GuiScreen.class); + constructor.setAccessible(true); + final GuiScreen directory = (GuiScreen) constructor.newInstance(menu); + final Field entries = directoryType.getDeclaredField("entries"); + entries.setAccessible(true); + for (final Object entry : (Iterable) entries.get(directory)) { + final Field modId = entry.getClass().getDeclaredField("modId"); + modId.setAccessible(true); + if ("modernmetals".equals(modId.get(entry))) { + minecraft.displayGuiScreen(directory); + final Method open = directoryType.getDeclaredMethod("open", entry.getClass()); + open.setAccessible(true); + open.invoke(directory, entry); + require(minecraft.currentScreen instanceof GuiConfig, "OreSpawn did not open the config"); + screen = (GuiConfig) minecraft.currentScreen; + menu = directory; + phase++; + return; + } + } + throw new IllegalStateException("Modern Metals is absent from OreSpawn's Mods list"); + } else if (phase == 2) { + verifyScreen(screen, menu); + for (final String path : new String[] { "aluminum_plate", "boron_ore", "titanium_ore" }) { + final ResourceLocation id = new ResourceLocation("modernmetals", path); + require(minecraft.getBlockRendererDispatcher().getBlockModelShapes().getModelForState( + ForgeRegistries.BLOCKS.getValue(id).getDefaultState()) + != minecraft.getRenderItem().getItemModelMesher().getModelManager().getMissingModel(), + "Missing block model " + id); + } + Files.write(Paths.get("modernmetals-client-pass.txt"), + Collections.singleton("Forge and OreSpawn screens and three baked block models passed."), + StandardCharsets.UTF_8); + phase++; + minecraft.shutdown(); + } + } + + private static void verifyScreen(final GuiConfig screen, final GuiScreen parent) { + require(screen.parentScreen == parent && screen.allRequireMcRestart, "Wrong config parent or restart policy"); + require(I18n.hasKey("config.modernmetals.title"), "Missing localized title"); + final boolean hasMode = screen.configElements.stream().anyMatch(element -> "contentMode".equals(element.getName())); + require(hasMode != ContentPolicy.inheritsBaseMetals(), "Fantasy selector belongs to the wrong mod"); + } + + private static void require(final boolean passes, final String message) { + if (!passes) throw new IllegalStateException(message); + } +} diff --git a/tools/validation/legacy112/runtime-probe/review/modern/ModernMetalsProbe.java b/tools/validation/legacy112/runtime-probe/review/modern/ModernMetalsProbe.java new file mode 100644 index 00000000..d99f3f7e --- /dev/null +++ b/tools/validation/legacy112/runtime-probe/review/modern/ModernMetalsProbe.java @@ -0,0 +1,164 @@ +package review.modern; + +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; +import com.mcmoddev.lib.registry.CrusherRecipeRegistry; +import com.mcmoddev.lib.registry.recipe.ICrusherRecipe; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; +import java.util.stream.Collectors; +import net.minecraft.block.Block; +import net.minecraft.entity.passive.EntityPig; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.FurnaceRecipes; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.WorldServer; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidUtil; +import net.minecraftforge.fml.common.FMLCommonHandler; +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.event.FMLServerStartedEvent; +import net.minecraftforge.fml.common.registry.ForgeRegistries; +import net.minecraftforge.oredict.OreDictionary; +import zone.moddev.mc.modernmetals.content.ContentPolicy; +import zone.moddev.mc.modernmetals.worldgen.ModernMetalsWorldgenProvider; +import zone.moddev.mc.orespawn.api.OreSpawnApi; + +@Mod(modid = "modernmetalsreview", version = "1", dependencies = "required-after:modernmetals") +public final class ModernMetalsProbe { + @Mod.EventHandler + public void clientInit(final FMLInitializationEvent event) { + if (event.getSide().isClient() && Boolean.getBoolean("review.client")) { + ClientChecks.install(); + } + } + + @Mod.EventHandler + public void verify(final FMLServerStartedEvent event) throws Exception { + final WorldServer world = FMLCommonHandler.instance().getMinecraftServerInstance().getWorld(0); + final boolean low = Boolean.getBoolean("review.low"); + require(ContentPolicy.active().lowFantasy() == low, "Wrong active Fantasy Level"); + require(ContentPolicy.inheritsBaseMetals() == Boolean.getBoolean("review.inherited"), + "Wrong Fantasy Level owner"); + if (Boolean.getBoolean("review.historical")) { + final String[] expected = { "boron_ore", "titanium_ore", "aluminum_plate" }; + for (int x = 0; x < expected.length; x++) { + final Block saved = world.getBlockState(new BlockPos(x, 180, 0)).getBlock(); + require(id(expected[x]).equals(saved.getRegistryName()), "Legacy block did not survive: " + expected[x]); + } + } + + final List ores = ForgeRegistries.BLOCKS.getValuesCollection().stream() + .filter(block -> block.getRegistryName() != null + && block.getRegistryName().getNamespace().equals("modernmetals") + && block.getRegistryName().getPath().endsWith("_ore")) + .collect(Collectors.toList()); + for (final Block ore : ores) { + final ResourceLocation id = ore.getRegistryName(); + final String name = id.getPath().replaceFirst("_ore$", ""); + final ICrusherRecipe recipe = CrusherRecipeRegistry.getRecipeForInputItem(new ItemStack(ore)); + require(recipe != null && !recipe.getOutput().isEmpty() + && recipe.getOutput().getItem().getRegistryName().equals(id(name + "_powder")), + "Shared crusher cannot resolve " + id); + } + + final boolean disabled = Boolean.getBoolean("review.boronDisabled"); + require(ForgeRegistries.BLOCKS.containsKey(id("boron_ore")) != disabled, "Boron toggle failed"); + for (final String name : new String[] { "beryllium", "thorium", "titanium" }) { + final Fluid fluid = FluidRegistry.getFluid(name); + require(fluid != null && fluid.getBlock() != null, "Missing molten " + name); + require(!FluidUtil.getFilledBucket(new FluidStack(fluid, 1000)).isEmpty(), "Missing " + name + " bucket"); + } + + final Block molten = ForgeRegistries.BLOCKS.getValue(id("titanium")); + final EntityPig pig = new EntityPig(world); + final BlockPos position = world.getSpawnPoint().up(3); + pig.setPosition(position.getX() + 0.5, position.getY(), position.getZ() + 0.5); + final float health = pig.getHealth(); + molten.onEntityCollision(world, position, molten.getDefaultState(), pig); + require(pig.getHealth() < health && pig.isBurning(), "Molten metal contact did not damage the entity"); + + for (final String recipe : new String[] { "plutonium_pickaxe", "uranium_sword", "rutile_axe", + "plutonium_fishingrod", "plutonium_horsearmor", "plutonium_pressureplate" }) { + final IRecipe entry = ForgeRegistries.RECIPES.getValue(id(recipe)); + require(entry != null && entry.getRecipeOutput().isEmpty() == low, "Wrong policy for " + recipe); + } + final IRecipe titanium = ForgeRegistries.RECIPES.getValue(id("titanium_blend_base")); + require(titanium != null && titanium.getRecipeOutput().getItem() == item("titanium_blend"), + "Titanium blend recipe missing"); + require(FurnaceRecipes.instance().getSmeltingResult(new ItemStack(item("titanium_blend"))).getItem() + == item("titanium_ingot"), "Titanium blend cannot be smelted"); + if (ContentPolicy.inheritsBaseMetals()) { + for (final String material : new String[] { "stainlesssteel", "nichrome" }) { + final IRecipe recipe = ForgeRegistries.RECIPES.getValue(id(material + "_blend_base")); + require(recipe != null && recipe.getIngredients().stream() + .allMatch(ingredient -> ingredient.getMatchingStacks().length > 0), + "Missing alloy ingredients for " + material); + require(FurnaceRecipes.instance().getSmeltingResult(new ItemStack(item(material + "_blend"))).getItem() + == item(material + "_ingot"), "Cannot smelt " + material); + } + } + if (Loader.isModLoaded("tconstruct")) { + TinkersChecks.verify(low); + } + for (final String alias : new String[] { "oreAluminum", "oreAluminium", "oreBauxite", "oreChrome" }) { + final Item ore = item(alias.equals("oreChrome") ? "chromium_ore" : "aluminum_ore"); + require(OreDictionary.getOres(alias).stream().anyMatch(stack -> stack.getItem() == ore), + "Missing ore alias " + alias); + } + + require(OreSpawnApi.isOreTakeoverActive("modernmetals"), "OreSpawn takeover is inactive"); + final JsonObject profile = OreSpawnApi.getActiveProfile(world.getMinecraftServer()).get().toJson(); + final Block titaniumOre = ForgeRegistries.BLOCKS.getValue(id("titanium_ore")); + final Block rutileOre = ForgeRegistries.BLOCKS.getValue(id("rutile_ore")); + final BlockPos.MutableBlockPos sample = new BlockPos.MutableBlockPos(); + final int centerX = world.getSpawnPoint().getX(); + final int centerZ = world.getSpawnPoint().getZ(); + int titaniumBlocks = 0; + int rutileBlocks = 0; + for (int x = centerX - 32; x < centerX + 32; x++) { + for (int z = centerZ - 32; z < centerZ + 32; z++) { + for (int y = 0; y <= 63; y++) { + final Block block = world.getBlockState(sample.setPos(x, y, z)).getBlock(); + if (block == titaniumOre) titaniumBlocks++; + if (block == rutileOre) rutileBlocks++; + } + } + } + require((titaniumBlocks > 0) == Boolean.getBoolean("review.titanium"), "Wrong titanium ore placement"); + require(rutileBlocks > 0, "Rutile ore did not generate"); + final JsonObject report = new JsonObject(); + report.addProperty("ore_count", ores.size()); + report.addProperty("titanium_blocks", titaniumBlocks); + report.addProperty("rutile_blocks", rutileBlocks); + report.addProperty("low_fantasy", low); + report.add("profile", profile); + report.add("provider", ModernMetalsWorldgenProvider.build(ForgeRegistries.BLOCKS::containsKey).get().toJson()); + Files.write(Paths.get("modernmetals-review.json"), new GsonBuilder().setPrettyPrinting().create() + .toJson(report).getBytes(StandardCharsets.UTF_8)); + world.getMinecraftServer().initiateShutdown(); + } + + private static Item item(final String path) { + final Item item = ForgeRegistries.ITEMS.getValue(id(path)); + require(item != null, "Missing " + path); + return item; + } + + private static ResourceLocation id(final String path) { + return new ResourceLocation("modernmetals", path); + } + + private static void require(final boolean passes, final String message) { + if (!passes) throw new IllegalStateException(message); + } +} diff --git a/tools/validation/legacy112/runtime-probe/review/modern/TinkersChecks.java b/tools/validation/legacy112/runtime-probe/review/modern/TinkersChecks.java new file mode 100644 index 00000000..8ddd5954 --- /dev/null +++ b/tools/validation/legacy112/runtime-probe/review/modern/TinkersChecks.java @@ -0,0 +1,42 @@ +package review.modern; + +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fml.common.registry.ForgeRegistries; +import slimeknights.tconstruct.library.TinkerRegistry; +import slimeknights.tconstruct.library.materials.Material; +import slimeknights.tconstruct.library.smeltery.CastingRecipe; +import slimeknights.tconstruct.library.tools.IToolPart; + +final class TinkersChecks { + static void verify(final boolean low) { + final Fluid titanium = FluidRegistry.getFluid("titanium"); + final Material material = TinkerRegistry.getMaterial("titanium"); + require(material != Material.UNKNOWN && material.getFluid() == titanium, "Titanium tool material is missing"); + final ItemStack ingot = new ItemStack(ForgeRegistries.ITEMS.getValue( + new ResourceLocation("modernmetals", "titanium_ingot"))); + require(TinkerRegistry.getMelting(ingot) != null, "Titanium ingot cannot be melted"); + require(TinkerRegistry.getAlloys().stream().anyMatch(alloy -> + alloy.getResult().getFluid() == titanium && alloy.getFluids().size() == 2 + && alloy.getFluids().stream().anyMatch(fluid -> "rutile".equals(fluid.getFluid().getName())) + && alloy.getFluids().stream().anyMatch(fluid -> "magnesium".equals(fluid.getFluid().getName()))), + "Missing titanium smeltery alloy"); + require(TinkerRegistry.getAllTableCastingRecipes().stream().filter(CastingRecipe.class::isInstance) + .map(CastingRecipe.class::cast).map(CastingRecipe::getResult) + .anyMatch(stack -> ItemStack.areItemsEqual(stack, ingot)), "Titanium ingot cannot be cast"); + final boolean plutoniumParts = TinkerRegistry.getAllTableCastingRecipes().stream() + .filter(CastingRecipe.class::isInstance).map(CastingRecipe.class::cast) + .map(CastingRecipe::getResult) + .anyMatch(stack -> stack.getItem() instanceof IToolPart + && "plutonium".equals(((IToolPart) stack.getItem()).getMaterialID(stack))); + require(plutoniumParts != low, "Wrong Low Fantasy policy for plutonium tool parts"); + } + + private static void require(final boolean passes, final String message) { + if (!passes) throw new IllegalStateException(message); + } + + private TinkersChecks() { } +} diff --git a/tools/validation/requirements.txt b/tools/validation/requirements.txt new file mode 100644 index 00000000..ad5d466f --- /dev/null +++ b/tools/validation/requirements.txt @@ -0,0 +1,2 @@ +jsonschema==4.23.0 +PyYAML==6.0.3