diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9fb4fda5..4297c86a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -172,7 +172,7 @@ jobs: 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/libs/ModernMetals-2.5.2.112021{,-sources,-javadoc}.jar "$bundle/" cp build/release/SHA256SUMS CHANGELOG.txt "$bundle/" ./gradlew verifyPreparedReleaseArtifacts verifyMavenCoordinates \ -PpreparedReleaseDir="$bundle" \ @@ -205,9 +205,9 @@ jobs: 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/libs/ModernMetals-2.5.2.112021.jar + build/libs/ModernMetals-2.5.2.112021-sources.jar + build/libs/ModernMetals-2.5.2.112021-javadoc.jar build/release/SHA256SUMS CHANGELOG.txt diff --git a/CHANGELOG.txt b/CHANGELOG.txt index e155d9d0..3378cd5b 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,12 @@ +2.5.2.112021 for 1.12.2 + +Use the published MMDLib 1.0.0-rc3 and MMD OreSpawn 4.1.6.112021. +Checked alongside Base Metals 2.6.2.112021. +Molten metal now burns on contact with the liquid surface. +Restored missing German, Russian and Simplified Chinese translations. +Fixed black backgrounds on ore overlays so the stone texture shows through. +Old 1.12.2 worlds can load past obsolete crusher recipes with empty outputs. + 2.5.1.112021 for 1.12.2 - Updated the build to ForgeGradle 7 and moved Modern Metals to the ModDev Zone namespace. diff --git a/README.md b/README.md index 8a695443..f623e251 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,10 @@ 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 +For Modern Metals 2.5.2.112021, install Forge 14.23.5.2859, +[MMDLib 1.0.0-rc3][mmdlib] and +[OreSpawn 4.1.6.112021][orespawn]. Modern Metals also works alongside +[Base Metals 2.6.2.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 @@ -20,15 +21,19 @@ 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. +Raw OreSpawn 1 files need to be converted on 1.10.2 or 1.11.2 before +moving the saved OreSpawn 4 profile to 1.12.2. -[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/ +[mmdlib]: https://www.curseforge.com/minecraft/mc-mods/mmdlib/files/9414723 +[orespawn]: https://www.curseforge.com/minecraft/mc-mods/mmd-orespawn/files/9527784 +[basemetals]: https://www.curseforge.com/minecraft/mc-mods/base-metals/files/9532211 ## 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 +the game and compiler use Java 8. Before importing into Eclipse or building, +run `python gradle/stage-legacy-dependencies.py .gradle/legacy-dependencies` +from the project root. Then 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. diff --git a/gradle.properties b/gradle.properties index 6b585402..42e18c70 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,7 +12,7 @@ mappings_version=39-1.12 mod_id=modernmetals mod_group=zone.moddev.mc.modernmetals -mod_version=2.5.1.112021 +mod_version=2.5.2.112021 mod_artifact=ModernMetals loader_name=forge loader_code=1 @@ -26,8 +26,8 @@ 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) +orespawn_version=4.1.6.112021 +orespawn_version_range=[4.1.6.112021,5.0.0) -mmdlib_version=1.0.0-rc2.36 +mmdlib_version=1.0.0-rc3 forge_installer_sha256=DBBA1CD1BF34A4B82148BAC50A6150F63FFCB2D5DC7D88A0508C27FDCFF15B76 diff --git a/gradle/dependencies.json b/gradle/dependencies.json index 5982e876..ae939bc9 100644 --- a/gradle/dependencies.json +++ b/gradle/dependencies.json @@ -1,23 +1,23 @@ [ { "id": "mmdlib", - "coordinate": "curse.maven:mmdlib-261744:2712947", + "coordinate": "curse.maven:mmdlib-261744:9414723", "repository": "https://www.cursemaven.com", - "sha256": "FE2229E6755A5FD306C33CC22DCCF055378D371339133E4912C5DC9213E360AF", + "sha256": "B35CD5A4454B03D43744EE263BD92A4B718660557C360F961B65A6303A80F88C", "runtime": true }, { "id": "orespawn", - "coordinate": "zone.moddev.mc.orespawn:OreSpawn:4.1.3.112021", - "repository": "https://maven.moddev.zone/releases", - "sha256": "5B5929CCDEB055679FD6D7B9629BF94F06085A6ECD791FD08D0500F666C079DD", + "coordinate": "curse.maven:mmd-orespawn-245586:9527784", + "repository": "https://www.cursemaven.com", + "sha256": "DA6B243317E3365371C397CC9A895D108312F3437FB1797FBB5E8241525EA75F", "runtime": true }, { "id": "basemetals", - "coordinate": "zone.moddev.mc.basemetals:BaseMetals:2.6.1.112021", - "repository": "https://maven.moddev.zone/releases", - "sha256": "FBAE982E308DCC1AC4281A5372560D06C8BB3A027EE290EDA4AC6875F5680FDC", + "coordinate": "curse.maven:base-metals-240967:9532211", + "repository": "https://www.cursemaven.com", + "sha256": "0CFD379FA059974E8DBA2AC743FF3E1E8E8183E48DA87BD63377E2EF0FB30312", "runtime": false }, { diff --git a/gradle/release/artifacts.gradle b/gradle/release/artifacts.gradle index 4b4030ff..87b2962e 100644 --- a/gradle/release/artifacts.gradle +++ b/gradle/release/artifacts.gradle @@ -38,7 +38,7 @@ tasks.register('verifyReleaseConfiguration') { group = 'verification' dependsOn tasks.named('verifyJava8Toolchain') doLast { - if (project.group != 'zone.moddev.mc.modernmetals' || project.version != '2.5.1.112021' + if (project.group != 'zone.moddev.mc.modernmetals' || project.version != '2.5.2.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.') @@ -47,8 +47,8 @@ tasks.register('verifyReleaseConfiguration') { 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)' + if (project.orespawn_version != '4.1.6.112021' + || project.orespawn_version_range != '[4.1.6.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.') diff --git a/src/main/java/zone/moddev/mc/modernmetals/ModernMetals.java b/src/main/java/zone/moddev/mc/modernmetals/ModernMetals.java index 34a2b367..3afa7dcb 100644 --- a/src/main/java/zone/moddev/mc/modernmetals/ModernMetals.java +++ b/src/main/java/zone/moddev/mc/modernmetals/ModernMetals.java @@ -29,7 +29,7 @@ modid = ModernMetals.MODID, name = ModernMetals.NAME, version = ModernMetals.VERSION, - 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", + dependencies = "required-after:forge@[14.23.5.2859,);required-after:mmdlib@[1.0.0-rc2,2.0.0);required-after:orespawn@[4.1.6.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") @@ -49,7 +49,7 @@ 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.1.112021"; + protected static final String VERSION = "2.5.2.112021"; protected static final String UPDATEJSON = SharedStrings.UPDATE_JSON_URL + "ModernMetals/master/update.json"; diff --git a/src/main/java/zone/moddev/mc/modernmetals/block/OverlayOreBlock.java b/src/main/java/zone/moddev/mc/modernmetals/block/OverlayOreBlock.java new file mode 100644 index 00000000..51154998 --- /dev/null +++ b/src/main/java/zone/moddev/mc/modernmetals/block/OverlayOreBlock.java @@ -0,0 +1,20 @@ +package zone.moddev.mc.modernmetals.block; + +import com.mcmoddev.lib.block.BlockMMDOre; +import com.mcmoddev.lib.material.MMDMaterial; +import net.minecraft.util.BlockRenderLayer; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +/** Lets the stone show through the ore texture's transparent pixels. */ +public final class OverlayOreBlock extends BlockMMDOre { + public OverlayOreBlock(final MMDMaterial material) { + super(material); + } + + @Override + @SideOnly(Side.CLIENT) + public BlockRenderLayer getRenderLayer() { + return BlockRenderLayer.CUTOUT_MIPPED; + } +} diff --git a/src/main/java/zone/moddev/mc/modernmetals/init/Blocks.java b/src/main/java/zone/moddev/mc/modernmetals/init/Blocks.java index 370f793f..8cfb063c 100644 --- a/src/main/java/zone/moddev/mc/modernmetals/init/Blocks.java +++ b/src/main/java/zone/moddev/mc/modernmetals/init/Blocks.java @@ -7,7 +7,9 @@ import com.mcmoddev.lib.data.SharedStrings; import com.mcmoddev.lib.init.Materials; import com.mcmoddev.lib.material.MMDMaterial; +import com.mcmoddev.lib.util.Oredicts; import zone.moddev.mc.modernmetals.ModernMetals; +import zone.moddev.mc.modernmetals.block.OverlayOreBlock; import zone.moddev.mc.modernmetals.data.MaterialNames; import net.minecraft.block.Block; @@ -43,13 +45,30 @@ public static void init() { .filter(materialName -> !Materials.getMaterialByName(materialName).isEmpty()) .forEach(materialName -> { final MMDMaterial material = Materials.getMaterialByName(materialName); - Arrays.asList(Names.BLOCK, Names.PLATE, Names.ORE, Names.BARS, Names.DOOR, Names.TRAPDOOR, + create(Names.BLOCK, material); + create(Names.PLATE, material); + createOre(material); + Arrays.asList(Names.BARS, Names.DOOR, Names.TRAPDOOR, Names.BUTTON, Names.SLAB, Names.DOUBLE_SLAB, Names.LEVER, Names.PRESSURE_PLATE, Names.STAIRS, Names.WALL).stream() .forEach(name -> create(name, material)); }); } + private static void createOre(final MMDMaterial material) { + if (MaterialNames.THORIUM.equals(material.getName())) { + create(Names.ORE, material); + } else if (material.hasOre() && !material.hasBlock(Names.ORE) && isNameEnabled(Names.ORE)) { + final Block ore = addBlock(new OverlayOreBlock(material), Names.ORE, material, + ItemGroups.getTab(SharedStrings.TAB_BLOCKS)); + material.addNewBlock(Names.ORE, ore); + final String prefix = getOredictFromName(Names.ORE); + if (prefix != null) { + Oredicts.registerOre(prefix + material.getCapitalizedName(), ore); + } + } + } + private static boolean filterFunc(final Block block) { return block.getRegistryName().getNamespace().equals(ModernMetals.MODID); } diff --git a/src/main/java/zone/moddev/mc/modernmetals/init/Fluids.java b/src/main/java/zone/moddev/mc/modernmetals/init/Fluids.java index eae747fe..83ad1837 100644 --- a/src/main/java/zone/moddev/mc/modernmetals/init/Fluids.java +++ b/src/main/java/zone/moddev/mc/modernmetals/init/Fluids.java @@ -5,13 +5,19 @@ import javax.annotation.Nonnull; -import com.mcmoddev.lib.block.InteractiveFluidBlock; +import com.mcmoddev.lib.block.BlockMoltenFluid; 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 zone.moddev.mc.modernmetals.data.MaterialNames; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; import net.minecraft.util.DamageSource; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraftforge.fluids.Fluid; /** * This class initializes all fluids in Modern Metals. @@ -39,16 +45,34 @@ public static void init() { .filter(Options::isFluidEnabled).forEach(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); - } - })); + material.setFluidBlockGetter(name -> new MoltenMetalBlock(material.getFluid())); addFluidBlock(material); }); } + private static final class MoltenMetalBlock extends BlockMoltenFluid { + MoltenMetalBlock(final Fluid fluid) { + super(fluid); + } + + @Override + public void onEntityCollision(final World world, final BlockPos pos, final IBlockState state, + final Entity entity) { + super.onEntityCollision(world, pos, state, entity); + if (world.isRemote || !(entity instanceof EntityLivingBase) || entity.isImmuneToFire()) { + return; + } + final double surface = pos.getY() + getFluidHeightForRender(world, pos, world.getBlockState(pos.up())); + if (entity.getEntityBoundingBox().minY >= surface || entity.isInLava()) { + return; + } + + // Minecraft handles damage once the entity enters the deeper lava contact bounds. + entity.attackEntityFrom(DamageSource.LAVA, 4.0F); + entity.setFire(15); + } + } + private static int getTemperature(@Nonnull final String name) { switch (name) { case MaterialNames.ALUMINUM: diff --git a/src/main/java/zone/moddev/mc/modernmetals/util/EventHandler.java b/src/main/java/zone/moddev/mc/modernmetals/util/EventHandler.java index b9738deb..64896d7d 100644 --- a/src/main/java/zone/moddev/mc/modernmetals/util/EventHandler.java +++ b/src/main/java/zone/moddev/mc/modernmetals/util/EventHandler.java @@ -4,10 +4,13 @@ import zone.moddev.mc.modernmetals.init.Fluids; import zone.moddev.mc.modernmetals.init.Items; import zone.moddev.mc.modernmetals.init.Materials; +import com.google.common.collect.ImmutableSet; 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.lib.registry.recipe.ICrusherRecipe; +import java.util.Set; import zone.moddev.mc.modernmetals.ModernMetals; @@ -15,10 +18,14 @@ import net.minecraft.item.Item; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.fml.common.Mod.EventBusSubscriber; +import net.minecraftforge.fml.common.eventhandler.EventPriority; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; @EventBusSubscriber(modid=ModernMetals.MODID) public class EventHandler { + private static final Set LEGACY_EMPTY_CRUSHER_RECIPES = ImmutableSet.of( + "blocklapis_to_air", "orelapis_to_air", "blockredstone_to_air", + "oreredstone_to_air", "quartz_block_to_air", "orequartz_to_air", "stone_slab_to_air"); @SubscribeEvent public static void mmdlibRegisterMaterials(final MMDLibRegisterMaterials event) { @@ -40,6 +47,19 @@ public static void mmdlibRegisterFluids(final MMDLibRegisterFluids event) { Fluids.init(); } + @SubscribeEvent(priority = EventPriority.LOWEST) + public static void onRemapLegacyCrusherRecipes(final RegistryEvent.MissingMappings event) { + for (final RegistryEvent.MissingMappings.Mapping mapping : event.getAllMappings()) { + // Older builds registered these empty outputs under either namespace. + final String namespace = mapping.key.getNamespace(); + if (mapping.getAction() == RegistryEvent.MissingMappings.Action.DEFAULT + && ("mmdlib".equals(namespace) || ModernMetals.MODID.equals(namespace)) + && LEGACY_EMPTY_CRUSHER_RECIPES.contains(mapping.key.getPath())) { + mapping.ignore(); + } + } + } + /** * * @param event The Event. diff --git a/src/main/resources/assets/modernmetals/lang/de_DE.lang b/src/main/resources/assets/modernmetals/lang/de_DE.lang index 1544ff4d..8273c497 100644 --- a/src/main/resources/assets/modernmetals/lang/de_DE.lang +++ b/src/main/resources/assets/modernmetals/lang/de_DE.lang @@ -1350,3 +1350,190 @@ 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. + +itemGroup.modernmetals.combat=Modern Metals – Kampf +material.beryllium.name=Beryllium +item.modernmetals.beryllium_arrow.name=Berylliumpfeil +item.modernmetals.beryllium_axe.name=Berylliumaxt +item.modernmetals.beryllium_bars.name=Berylliumgitter +item.modernmetals.beryllium_blend.name=Berylliumgemisch +item.modernmetals.beryllium_block.name=Berylliumblock +item.modernmetals.beryllium_boots.name=Berylliumstiefel +item.modernmetals.beryllium_bolt.name=Berylliumbolzen +item.modernmetals.beryllium_bow.name=Berylliumbogen +item.modernmetals.beryllium_chestplate.name=Berylliumbrustpanzer +item.modernmetals.beryllium_crackhammer.name=Berylliumbruchhammer +item.modernmetals.beryllium_crossbow.name=Berylliumarmbrust +item.modernmetals.beryllium_door.name=Berylliumtür +item.modernmetals.beryllium_fishing_rod.name=Berylliumangel +item.modernmetals.beryllium_gear.name=Berylliumzahnrad +item.modernmetals.beryllium_helmet.name=Berylliumhelm +item.modernmetals.beryllium_hoe.name=Berylliumhacke +item.modernmetals.beryllium_horse_armor.name=Berylliumpferderüstung +item.modernmetals.beryllium_ingot.name=Berylliumbarren +item.modernmetals.beryllium_leggings.name=Berylliumbeinrüstung +item.modernmetals.beryllium_nugget.name=Berylliumnugget +item.modernmetals.beryllium_ore.name=Berylliumerz +item.modernmetals.beryllium_pickaxe.name=Berylliumspitzhacke +item.modernmetals.beryllium_powder.name=Berylliumpulver +item.modernmetals.beryllium_rod.name=Berylliumstange +item.modernmetals.beryllium_shears.name=Berylliumschere +item.modernmetals.beryllium_shield.name=Berylliumschild +item.modernmetals.beryllium_shovel.name=Berylliumschaufel +item.modernmetals.beryllium_smallblend.name=Kleiner Berylliumgemischhaufen +item.modernmetals.beryllium_smallpowder.name=Kleiner Berylliumpulverhaufen +item.modernmetals.beryllium_sword.name=Berylliumschwert +item.modernmetals.beryllium_trapdoor.name=Berylliumfalltür +material.boron.name=Bor +item.modernmetals.boron_arrow.name=Borpfeil +item.modernmetals.boron_axe.name=Boraxt +item.modernmetals.boron_bars.name=Borgitter +item.modernmetals.boron_blend.name=Borgemisch +item.modernmetals.boron_block.name=Borblock +item.modernmetals.boron_boots.name=Borstiefel +item.modernmetals.boron_bolt.name=Borbolzen +item.modernmetals.boron_bow.name=Borbogen +item.modernmetals.boron_chestplate.name=Borbrustpanzer +item.modernmetals.boron_crackhammer.name=Borbruchhammer +item.modernmetals.boron_crossbow.name=Borarmbrust +item.modernmetals.boron_door.name=Bortür +item.modernmetals.boron_fishing_rod.name=Borangel +item.modernmetals.boron_gear.name=Borzahnrad +item.modernmetals.boron_helmet.name=Borhelm +item.modernmetals.boron_hoe.name=Borhacke +item.modernmetals.boron_horse_armor.name=Borpferderüstung +item.modernmetals.boron_ingot.name=Borbarren +item.modernmetals.boron_leggings.name=Borbeinrüstung +item.modernmetals.boron_nugget.name=Bornugget +item.modernmetals.boron_ore.name=Borerz +item.modernmetals.boron_pickaxe.name=Borspitzhacke +item.modernmetals.boron_powder.name=Borpulver +item.modernmetals.boron_rod.name=Borstange +item.modernmetals.boron_shears.name=Borschere +item.modernmetals.boron_shield.name=Borschild +item.modernmetals.boron_shovel.name=Borschaufel +item.modernmetals.boron_smallblend.name=Kleiner Borgemischhaufen +item.modernmetals.boron_smallpowder.name=Kleiner Borpulverhaufen +item.modernmetals.boron_sword.name=Borschwert +item.modernmetals.boron_trapdoor.name=Borfalltür +tile.modernmetals.boron_bars.name=Borgitter +tile.modernmetals.boron_block.name=Bor +tile.modernmetals.boron_door.name=Bortür +tile.modernmetals.boron_ore.name=Borerz +tile.modernmetals.boron_plate.name=Borblech +tile.modernmetals.boron_trapdoor.name=Borfalltür +fluid.modernmetals.boron=Geschmolzenes Bor +fluid.modernmetals.beryllium=Geschmolzenes Beryllium +fluid.modernmetals.boron.name=Geschmolzenes Bor +fluid.modernmetals.beryllium.name=Geschmolzenes Beryllium +tile.modernmetals.boron.name=Geschmolzenes Bor +tile.modernmetals.beryllium_bars.name=Berylliumgitter +tile.modernmetals.beryllium_block.name=Beryllium +tile.modernmetals.beryllium_door.name=Berylliumtür +tile.modernmetals.beryllium_ore.name=Berylliumerz +tile.modernmetals.beryllium_plate.name=Berylliumblech +tile.modernmetals.beryllium_trapdoor.name=Berylliumfalltür +item.modernmetals.beryllium_clump.name=Berylliumklumpen +item.modernmetals.beryllium_crushed.name=Zerkleinertes Berylliumerz +item.modernmetals.beryllium_crushed_purified.name=Gereinigtes, zerkleinertes Berylliumerz +item.modernmetals.beryllium_crystal.name=Berylliumkristall +item.modernmetals.beryllium_dense_plate.name=Verdichtetes Berylliumblech +item.modernmetals.beryllium_powder_dirty.name=Verunreinigtes Berylliumpulver +item.modernmetals.beryllium_shard.name=Berylliumsplitter +item.modernmetals.beryllium_button.name=Berylliumschalter +item.modernmetals.beryllium_lever.name=Berylliumhebel +item.modernmetals.beryllium_pressure_plate.name=Berylliumdruckplatte +item.modernmetals.beryllium_slab.name=Berylliumfliese +item.modernmetals.beryllium_stairs.name=Berylliumtreppe +item.modernmetals.beryllium_wall.name=Berylliummauer +tile.modernmetals.beryllium.name=Geschmolzenes Beryllium +tile.modernmetals.beryllium_button.name=Berylliumschalter +tile.modernmetals.beryllium_lever.name=Berylliumhebel +tile.modernmetals.beryllium_pressure_plate.name=Berylliumdruckplatte +tile.modernmetals.beryllium_slab.name=Berylliumfliese +tile.modernmetals.beryllium_stairs.name=Berylliumtreppe +tile.modernmetals.beryllium_wall.name=Berylliummauer +item.modernmetals.boron_clump.name=Borklumpen +item.modernmetals.boron_crushed.name=Zerkleinertes Borerz +item.modernmetals.boron_crushed_purified.name=Gereinigtes, zerkleinertes Borerz +item.modernmetals.boron_crystal.name=Borkristall +item.modernmetals.boron_dense_plate.name=Verdichtetes Borblech +item.modernmetals.boron_powder_dirty.name=Verunreinigtes Borpulver +item.modernmetals.boron_shard.name=Borsplitter +item.modernmetals.boron_button.name=Borschalter +item.modernmetals.boron_lever.name=Borhebel +item.modernmetals.boron_pressure_plate.name=Bordruckplatte +item.modernmetals.boron_slab.name=Borfliese +item.modernmetals.boron_stairs.name=Bortreppe +item.modernmetals.boron_wall.name=Bormauer +tile.modernmetals.boron_button.name=Borschalter +tile.modernmetals.boron_lever.name=Borhebel +tile.modernmetals.boron_pressure_plate.name=Bordruckplatte +tile.modernmetals.boron_slab.name=Borfliese +tile.modernmetals.boron_stairs.name=Bortreppe +tile.modernmetals.boron_wall.name=Bormauer +material.thorium.name=Thorium +item.modernmetals.thorium_arrow.name=Thoriumpfeil +item.modernmetals.thorium_axe.name=Thoriumaxt +item.modernmetals.thorium_bars.name=Thoriumgitter +item.modernmetals.thorium_blend.name=Thoriumgemisch +item.modernmetals.thorium_block.name=Thoriumblock +item.modernmetals.thorium_boots.name=Thoriumstiefel +item.modernmetals.thorium_bolt.name=Thoriumbolzen +item.modernmetals.thorium_bow.name=Thoriumbogen +item.modernmetals.thorium_chestplate.name=Thoriumbrustpanzer +item.modernmetals.thorium_crackhammer.name=Thoriumbruchhammer +item.modernmetals.thorium_crossbow.name=Thoriumarmbrust +item.modernmetals.thorium_door.name=Thoriumtür +item.modernmetals.thorium_fishing_rod.name=Thoriumangel +item.modernmetals.thorium_gear.name=Thoriumzahnrad +item.modernmetals.thorium_helmet.name=Thoriumhelm +item.modernmetals.thorium_hoe.name=Thoriumhacke +item.modernmetals.thorium_horse_armor.name=Thoriumpferderüstung +item.modernmetals.thorium_ingot.name=Thoriumbarren +item.modernmetals.thorium_leggings.name=Thoriumbeinrüstung +item.modernmetals.thorium_nugget.name=Thoriumnugget +item.modernmetals.thorium_ore.name=Thoriumerz +item.modernmetals.thorium_pickaxe.name=Thoriumspitzhacke +item.modernmetals.thorium_powder.name=Thoriumpulver +item.modernmetals.thorium_rod.name=Thoriumstange +item.modernmetals.thorium_shears.name=Thoriumschere +item.modernmetals.thorium_shield.name=Thoriumschild +item.modernmetals.thorium_shovel.name=Thoriumschaufel +item.modernmetals.thorium_smallblend.name=Kleiner Thoriumgemischhaufen +item.modernmetals.thorium_smallpowder.name=Kleiner Thoriumpulverhaufen +item.modernmetals.thorium_sword.name=Thoriumschwert +item.modernmetals.thorium_trapdoor.name=Thoriumfalltür +tile.modernmetals.thorium_bars.name=Thoriumgitter +tile.modernmetals.thorium_block.name=Thorium +tile.modernmetals.thorium_door.name=Thoriumtür +tile.modernmetals.thorium_ore.name=Thoriumerz +tile.modernmetals.thorium_plate.name=Thoriumblech +tile.modernmetals.thorium_trapdoor.name=Thoriumfalltür +fluid.modernmetals.thorium=Geschmolzenes Thorium +fluid.modernmetals.thorium.name=Geschmolzenes Thorium +tile.modernmetals.thorium.name=Geschmolzenes Thorium +item.modernmetals.thorium_clump.name=Thoriumklumpen +item.modernmetals.thorium_crushed.name=Zerkleinertes Thoriumerz +item.modernmetals.thorium_crushed_purified.name=Gereinigtes, zerkleinertes Thoriumerz +item.modernmetals.thorium_crystal.name=Thoriumkristall +item.modernmetals.thorium_dense_plate.name=Verdichtetes Thoriumblech +item.modernmetals.thorium_powder_dirty.name=Verunreinigtes Thoriumpulver +item.modernmetals.thorium_shard.name=Thoriumsplitter +item.modernmetals.thorium_button.name=Thoriumschalter +item.modernmetals.thorium_lever.name=Thoriumhebel +item.modernmetals.thorium_pressure_plate.name=Thoriumdruckplatte +item.modernmetals.thorium_slab.name=Thoriumfliese +item.modernmetals.thorium_stairs.name=Thoriumtreppe +item.modernmetals.thorium_wall.name=Thoriummauer +tile.modernmetals.thorium_button.name=Thoriumschalter +tile.modernmetals.thorium_lever.name=Thoriumhebel +tile.modernmetals.thorium_pressure_plate.name=Thoriumdruckplatte +tile.modernmetals.thorium_slab.name=Thoriumfliese +tile.modernmetals.thorium_stairs.name=Thoriumtreppe +tile.modernmetals.thorium_wall.name=Thoriummauer +item.modernmetals.titanium_casing.name=Titangehäuse +item.modernmetals.beryllium_scythe.name=Berylliumsichel +item.modernmetals.boron_scythe.name=Borsichel +item.modernmetals.thorium_scythe.name=Thoriumsichel +item.modernmetals.titanium_scythe.name=Titansichel diff --git a/src/main/resources/assets/modernmetals/lang/ru_RU.lang b/src/main/resources/assets/modernmetals/lang/ru_RU.lang index 9afd2809..d6ac0774 100644 --- a/src/main/resources/assets/modernmetals/lang/ru_RU.lang +++ b/src/main/resources/assets/modernmetals/lang/ru_RU.lang @@ -1547,3 +1547,10 @@ config.modernmetals.enable_zirconium=Включить цирконий config.modernmetals.content_mode.high_fantasy=Высокий уровень фэнтези config.modernmetals.content_mode.low_fantasy=Низкий уровень фэнтези config.modernmetals.inherited_mode=Уровень фэнтези задаётся в Base Metals. Перезапустите игру после изменений. + +itemGroup.modernmetals.combat=Modern Metals: боевое снаряжение +item.modernmetals.titanium_casing.name=Корпус из материала «Титан» +item.modernmetals.beryllium_scythe.name=Серп из материала «Бериллий» +item.modernmetals.boron_scythe.name=Серп из материала «Бор» +item.modernmetals.thorium_scythe.name=Серп из материала «Торий» +item.modernmetals.titanium_scythe.name=Серп из материала «Титан» diff --git a/src/main/resources/assets/modernmetals/lang/zh_CN.lang b/src/main/resources/assets/modernmetals/lang/zh_CN.lang index be30efe2..d97f9681 100644 --- a/src/main/resources/assets/modernmetals/lang/zh_CN.lang +++ b/src/main/resources/assets/modernmetals/lang/zh_CN.lang @@ -1547,3 +1547,10 @@ config.modernmetals.enable_zirconium=启用锆 config.modernmetals.content_mode.high_fantasy=高奇幻 config.modernmetals.content_mode.low_fantasy=低奇幻 config.modernmetals.inherited_mode=幻想等级由 Base Metals 设置。更改设置后请重新启动游戏。 + +itemGroup.modernmetals.combat=Modern Metals 战斗用品 +item.modernmetals.titanium_casing.name=钛外壳 +item.modernmetals.beryllium_scythe.name=铍镰刀 +item.modernmetals.boron_scythe.name=硼镰刀 +item.modernmetals.thorium_scythe.name=钍镰刀 +item.modernmetals.titanium_scythe.name=钛镰刀 diff --git a/tools/validation/legacy112/run_packaged_suite.py b/tools/validation/legacy112/run_packaged_suite.py index 88ded9fa..5b832b60 100644 --- a/tools/validation/legacy112/run_packaged_suite.py +++ b/tools/validation/legacy112/run_packaged_suite.py @@ -36,7 +36,7 @@ def run(java, directory, phase, flags): 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", + [str(java), "-Xmx1200M", *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, @@ -69,7 +69,7 @@ def main(): 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") + mod = Path("build/libs/ModernMetals-2.5.2.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() @@ -151,6 +151,12 @@ def main(): else: assert "oreBauxite" in group["ore_dictionary_entries"] assert run(args.java, target, "reload", flags) == first, name + if name == "os3-import": + remote_region = target / "world/region/r.6.-6.mca" + assert not remote_region.exists(), remote_region + later = run(args.java, target, "later", [*flags, "-Dreview.later=true"]) + assert remote_region.is_file() and later["profile"] == profile + print("OS3 profile preserved while generating later chunks", flush=True) if source_path: assert digest(source_path) == original, source_path print(f"{name} passed fresh world and reload", flush=True) diff --git a/tools/validation/legacy112/runtime-probe/review/modern/ClientChecks.java b/tools/validation/legacy112/runtime-probe/review/modern/ClientChecks.java index b567e0a9..606f8cda 100644 --- a/tools/validation/legacy112/runtime-probe/review/modern/ClientChecks.java +++ b/tools/validation/legacy112/runtime-probe/review/modern/ClientChecks.java @@ -8,7 +8,11 @@ import java.nio.file.Paths; import java.util.Collections; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiDisconnected; import net.minecraft.client.gui.GuiMainMenu; +import net.minecraft.client.multiplayer.GuiConnecting; +import net.minecraft.client.multiplayer.ServerData; +import net.minecraft.util.text.ITextComponent; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.resources.I18n; import net.minecraft.util.ResourceLocation; @@ -24,7 +28,12 @@ public final class ClientChecks { public static void install() { - MinecraftForge.EVENT_BUS.register(new ClientChecks()); + if (Boolean.getBoolean("review.oreDisplay")) { + OreOverlayChecks.installDisplay(); + return; + } + final String server = System.getProperty("review.remoteServer"); + MinecraftForge.EVENT_BUS.register(server == null ? new ClientChecks() : new RemoteCheck(server)); } private GuiScreen menu; @@ -76,6 +85,7 @@ public void check(final TickEvent.ClientTickEvent event) throws Exception { != minecraft.getRenderItem().getItemModelMesher().getModelManager().getMissingModel(), "Missing block model " + id); } + OreOverlayChecks.verify(minecraft); Files.write(Paths.get("modernmetals-client-pass.txt"), Collections.singleton("Forge and OreSpawn screens and three baked block models passed."), StandardCharsets.UTF_8); @@ -84,6 +94,51 @@ public void check(final TickEvent.ClientTickEvent event) throws Exception { } } + public static final class RemoteCheck { + private final String address; + private final String expectedDisconnect = System.getProperty("review.expectedDisconnect", ""); + private final long deadline = System.currentTimeMillis() + 90000; + private boolean connecting; + private boolean finished; + + RemoteCheck(final String address) { + this.address = address; + } + + @SubscribeEvent + public void check(final TickEvent.ClientTickEvent event) throws Exception { + if (event.phase != TickEvent.Phase.END || finished) return; + final Minecraft minecraft = Minecraft.getMinecraft(); + if (!connecting && minecraft.currentScreen instanceof GuiMainMenu) { + minecraft.displayGuiScreen(new GuiConnecting(minecraft.currentScreen, minecraft, + new ServerData("Disposable Forge", address, false))); + connecting = true; + } else if (minecraft.world != null && minecraft.player != null) { + require(expectedDisconnect.isEmpty(), "Mismatched player entered the world"); + finish(minecraft, "Joined dedicated server"); + } else if (connecting && minecraft.currentScreen instanceof GuiDisconnected) { + for (final Field field : GuiDisconnected.class.getDeclaredFields()) { + if (field.getType() != ITextComponent.class) continue; + field.setAccessible(true); + final String reason = ((ITextComponent) field.get(minecraft.currentScreen)).getUnformattedText(); + require(!expectedDisconnect.isEmpty() && reason.contains(expectedDisconnect), reason); + finish(minecraft, reason); + return; + } + throw new IllegalStateException("Missing disconnect reason"); + } else { + require(System.currentTimeMillis() < deadline, "Dedicated server login timed out"); + } + } + + private void finish(final Minecraft minecraft, final String result) throws Exception { + Files.write(Paths.get("modernmetals-connection-pass.txt"), + Collections.singleton(result), StandardCharsets.UTF_8); + finished = true; + 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"); diff --git a/tools/validation/legacy112/runtime-probe/review/modern/ModernMetalsProbe.java b/tools/validation/legacy112/runtime-probe/review/modern/ModernMetalsProbe.java index d99f3f7e..eb639efa 100644 --- a/tools/validation/legacy112/runtime-probe/review/modern/ModernMetalsProbe.java +++ b/tools/validation/legacy112/runtime-probe/review/modern/ModernMetalsProbe.java @@ -2,15 +2,21 @@ import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; +import com.mojang.authlib.GameProfile; 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.UUID; import java.util.stream.Collectors; import net.minecraft.block.Block; -import net.minecraft.entity.passive.EntityPig; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.MoverType; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.MobEffects; +import net.minecraft.potion.PotionEffect; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.FurnaceRecipes; @@ -44,6 +50,7 @@ public void clientInit(final FMLInitializationEvent event) { @Mod.EventHandler public void verify(final FMLServerStartedEvent event) throws Exception { + if (Boolean.getBoolean("review.oreDisplay")) return; final WorldServer world = FMLCommonHandler.instance().getMinecraftServerInstance().getWorld(0); final boolean low = Boolean.getBoolean("review.low"); require(ContentPolicy.active().lowFantasy() == low, "Wrong active Fantasy Level"); @@ -79,13 +86,7 @@ public void verify(final FMLServerStartedEvent event) throws Exception { 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"); + verifyMoltenContact(world); for (final String recipe : new String[] { "plutonium_pickaxe", "uranium_sword", "rutile_axe", "plutonium_fishingrod", "plutonium_horsearmor", "plutonium_pressureplate" }) { @@ -121,8 +122,19 @@ public void verify(final FMLServerStartedEvent event) throws Exception { 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(); + final boolean later = Boolean.getBoolean("review.later"); + final int centerX = later ? 3104 : world.getSpawnPoint().getX(); + final int centerZ = later ? -3040 : world.getSpawnPoint().getZ(); + if (later) { + final int firstChunkX = (centerX - 32) >> 4; + final int firstChunkZ = (centerZ - 32) >> 4; + // Include the neighboring chunks so Forge populates the whole sample. + for (int x = firstChunkX - 1; x <= firstChunkX + 4; x++) { + for (int z = firstChunkZ - 1; z <= firstChunkZ + 4; z++) { + world.getChunk(x, z); + } + } + } int titaniumBlocks = 0; int rutileBlocks = 0; for (int x = centerX - 32; x < centerX + 32; x++) { @@ -148,6 +160,41 @@ public void verify(final FMLServerStartedEvent event) throws Exception { world.getMinecraftServer().initiateShutdown(); } + private static void verifyMoltenContact(final WorldServer world) { + final BlockPos pos = world.getSpawnPoint().up(4); + final IBlockState original = world.getBlockState(pos); + final Block molten = ForgeRegistries.BLOCKS.getValue(id("titanium")); + try { + world.setBlockState(pos, molten.getDefaultState(), 2); + checkContact(world, pos, "above source", 0.90, false, false, 0.0F); + checkContact(world, pos, "shallow source", 0.70, false, false, 4.0F); + checkContact(world, pos, "deep source", 0.25, false, false, 4.0F); + checkContact(world, pos, "fire resistance", 0.70, true, false, 0.0F); + checkContact(world, pos, "creative", 0.70, false, true, 0.0F); + world.setBlockState(pos, molten.getStateFromMeta(1), 2); + checkContact(world, pos, "above flowing metal", 0.80, false, false, 0.0F); + checkContact(world, pos, "shallow flowing metal", 0.70, false, false, 4.0F); + } finally { + world.setBlockState(pos, original, 2); + } + } + + private static void checkContact(final WorldServer world, final BlockPos pos, final String label, + final double height, final boolean resistant, final boolean creative, final float expectedDamage) { + final EntityPlayer player = new EntityPlayer(world, new GameProfile(UUID.randomUUID(), "FluidCheck")) { + @Override public boolean isSpectator() { return false; } + @Override public boolean isCreative() { return creative; } + }; + player.capabilities.disableDamage = creative; + if (resistant) player.addPotionEffect(new PotionEffect(MobEffects.FIRE_RESISTANCE, 200)); + player.setPosition(pos.getX() - 0.5, pos.getY() + height, pos.getZ() + 0.5); + player.move(MoverType.SELF, 1.0, 0.0, 0.0); + player.onEntityUpdate(); + final float damage = player.getMaxHealth() - player.getHealth(); + require(damage == expectedDamage && (expectedDamage == 0.0F || player.isBurning()), + label + " expected " + expectedDamage + " molten metal damage, got " + damage + ", burning=" + player.isBurning()); + } + private static Item item(final String path) { final Item item = ForgeRegistries.ITEMS.getValue(id(path)); require(item != null, "Missing " + path); diff --git a/tools/validation/legacy112/runtime-probe/review/modern/OreOverlayChecks.java b/tools/validation/legacy112/runtime-probe/review/modern/OreOverlayChecks.java new file mode 100644 index 00000000..474dd553 --- /dev/null +++ b/tools/validation/legacy112/runtime-probe/review/modern/OreOverlayChecks.java @@ -0,0 +1,130 @@ +package review.modern; + +import java.util.List; +import java.util.concurrent.Future; +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiMainMenu; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.block.model.IBakedModel; +import net.minecraft.init.Blocks; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.BlockRenderLayer; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.ScreenShotHelper; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.GameType; +import net.minecraft.world.WorldServer; +import net.minecraft.world.WorldSettings; +import net.minecraft.world.WorldType; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent; +import net.minecraftforge.fml.common.registry.ForgeRegistries; + +public final class OreOverlayChecks { + private static final String STONE = "minecraft:blocks/stone"; + private static final int TEST_STONE = 0xFF39B48E; + + static void verify(final Minecraft minecraft) { + final IBakedModel missing = minecraft.getRenderItem().getItemModelMesher().getModelManager().getMissingModel(); + int overlays = 0; + int opaque = 0; + for (final Block ore : ForgeRegistries.BLOCKS.getValuesCollection()) { + final ResourceLocation id = ore.getRegistryName(); + if (id == null || !"modernmetals".equals(id.getNamespace()) || !id.getPath().endsWith("_ore")) continue; + + final IBlockState state = ore.getDefaultState(); + final Item item = Item.getItemFromBlock(ore); + final boolean thorium = "thorium_ore".equals(id.getPath()); + require(id.equals(item.getRegistryName()), "Ore item registry ID differs for " + id); + final BlockRenderLayer layer = thorium ? BlockRenderLayer.SOLID : BlockRenderLayer.CUTOUT_MIPPED; + require(ore.canRenderInLayer(state, layer), id + " cannot render in " + layer + + " (block layer " + ore.getRenderLayer() + ")"); + + final IBakedModel block = minecraft.getBlockRendererDispatcher().getBlockModelShapes().getModelForState(state); + final IBakedModel inventory = minecraft.getRenderItem().getItemModelMesher().getItemModel(new ItemStack(item)); + require(block != missing && inventory != missing, "Missing baked model for " + id); + final String base = thorium ? "modernmetals:blocks/thorium_ore" : STONE; + final String overlay = thorium ? null : "modernmetals:blocks/ore_overlays/" + id.getPath(); + verifyFaces(block, state, base, overlay); + verifyFaces(inventory, null, base, overlay); + if (thorium) opaque++; else overlays++; + } + require(overlays == 16 && opaque == 1, "Unexpected ore set: " + overlays + " overlays and " + opaque + " opaque"); + final int stonePixel = minecraft.getTextureMapBlocks().getAtlasSprite(STONE).getFrameTextureData(0)[0][0]; + require((stonePixel == TEST_STONE) == Boolean.getBoolean("review.testStone"), "Unexpected stone texture"); + System.out.println("MODERN_METALS_ORE_OVERLAYS_PASS overlays=" + overlays + " opaque=" + opaque + + " replacementStone=" + Boolean.getBoolean("review.testStone")); + } + + private static void verifyFaces(final IBakedModel model, final IBlockState state, final String base, final String overlay) { + require(base.equals(model.getParticleTexture().getIconName()), "Wrong particle texture for " + base); + for (final EnumFacing face : EnumFacing.values()) { + final List quads = model.getQuads(state, face, 0); + require(quads.size() == (overlay == null ? 1 : 2), "Wrong face count for " + base + " " + face); + require(hasSprite(quads, base), "Missing base sprite " + base); + if (overlay != null) require(hasSprite(quads, overlay), "Missing overlay sprite " + overlay); + } + } + + private static boolean hasSprite(final List quads, final String name) { + return quads.stream().anyMatch(quad -> name.equals(quad.getSprite().getIconName())); + } + + static void installDisplay() { + MinecraftForge.EVENT_BUS.register(new OreOverlayChecks()); + } + + private boolean launched; + private Future display; + private int ticks; + + @SubscribeEvent + public void capture(final TickEvent.ClientTickEvent event) throws Exception { + if (event.phase != TickEvent.Phase.END) return; + final Minecraft minecraft = Minecraft.getMinecraft(); + if (!launched && minecraft.currentScreen instanceof GuiMainMenu) { + launched = true; + minecraft.launchIntegratedServer("ore-display", "Modern Metals ore display", + new WorldSettings(8675309L, GameType.CREATIVE, false, false, WorldType.FLAT)); + } else if (minecraft.world != null && minecraft.player != null) { + if (display == null) { + final MinecraftServer server = minecraft.getIntegratedServer(); + display = server.addScheduledTask(() -> placeDisplay(server)); + } else if (display.isDone() && ++ticks >= 60) { + display.get(); + minecraft.gameSettings.hideGUI = true; + ScreenShotHelper.saveScreenshot(minecraft.gameDir, "ore-overlay-display.png", + minecraft.displayWidth, minecraft.displayHeight, minecraft.getFramebuffer()); + verify(minecraft); + minecraft.shutdown(); + } + } + } + + private static void placeDisplay(final MinecraftServer server) { + final WorldServer world = server.getWorld(0); + world.setWorldTime(6000); + for (int x = -2; x <= 6; x++) { + for (int z = -6; z <= 3; z++) { + world.setBlockState(new BlockPos(x, 199, z), Blocks.STONE.getDefaultState()); + } + } + final String[] ores = {"aluminum_ore", "boron_ore", "rutile_ore", "thorium_ore"}; + for (int x = 0; x < ores.length; x++) { + final Block ore = ForgeRegistries.BLOCKS.getValue(new ResourceLocation("modernmetals", ores[x])); + world.setBlockState(new BlockPos(x, 200, 0), ore.getDefaultState()); + } + world.setBlockState(new BlockPos(4, 200, 0), Blocks.STONE.getDefaultState()); + server.getPlayerList().getPlayers().get(0).connection.setPlayerLocation(2.5, 200, -4, 0, 13); + } + + private static void require(final boolean condition, final String message) { + if (!condition) throw new IllegalStateException(message); + } +}