diff --git a/.gitignore b/.gitignore index 91c3a8b6..cf6e90b9 100644 --- a/.gitignore +++ b/.gitignore @@ -437,3 +437,4 @@ FodyWeavers.xsd /.specstory/ /.vscode/settings.json /Sentry/ +/downloads/ diff --git a/MinecraftClient/Mapping/BlockHardness.cs b/MinecraftClient/Mapping/BlockHardness.cs new file mode 100644 index 00000000..311507b0 --- /dev/null +++ b/MinecraftClient/Mapping/BlockHardness.cs @@ -0,0 +1,1326 @@ +using System.Collections.Frozen; +using System.Collections.Generic; + +namespace MinecraftClient.Mapping +{ + /// + /// Provides block hardness values and tool requirement data for mining calculations. + /// Data extracted from Minecraft 1.21.11 decompiled source (Blocks.java). + /// + public static class BlockHardness + { + /// + /// Default hardness for blocks not in the table (assumes stone-like). + /// + public const float DefaultHardness = 1.5f; + + /// + /// Get the hardness value for a block material. + /// Returns -1 for unbreakable blocks, 0 for instant-break blocks. + /// + public static float GetHardness(Material material) + { + if (HardnessTable.TryGetValue(material, out float hardness)) + return hardness; + return DefaultHardness; + } + + /// + /// Check whether a block requires the correct tool to get drops + /// (and uses the 100 divisor instead of 30 when mined without the correct tool). + /// + public static bool RequiresCorrectTool(Material material) + { + return RequiresCorrectToolSet.Contains(material); + } + + private static readonly FrozenDictionary HardnessTable = new Dictionary + { + // Hardness -1.0: 15 blocks + { Material.Barrier, -1.0f }, + { Material.Bedrock, -1.0f }, + { Material.ChainCommandBlock, -1.0f }, + { Material.CommandBlock, -1.0f }, + { Material.EndGateway, -1.0f }, + { Material.EndPortal, -1.0f }, + { Material.EndPortalFrame, -1.0f }, + { Material.Jigsaw, -1.0f }, + { Material.Light, -1.0f }, + { Material.MovingPiston, -1.0f }, + { Material.NetherPortal, -1.0f }, + { Material.RepeatingCommandBlock, -1.0f }, + { Material.StructureBlock, -1.0f }, + { Material.TestBlock, -1.0f }, + { Material.TestInstanceBlock, -1.0f }, + // Hardness 0.0: 443 blocks + { Material.AcaciaButton, 0.0f }, + { Material.AcaciaLeaves, 0.0f }, + { Material.AcaciaLog, 0.0f }, + { Material.AcaciaSapling, 0.0f }, + { Material.Air, 0.0f }, + { Material.Allium, 0.0f }, + { Material.AndesiteSlab, 0.0f }, + { Material.AndesiteWall, 0.0f }, + { Material.Azalea, 0.0f }, + { Material.AzaleaLeaves, 0.0f }, + { Material.AzureBluet, 0.0f }, + { Material.Bamboo, 0.0f }, + { Material.BambooBlock, 0.0f }, + { Material.BambooButton, 0.0f }, + { Material.BambooSapling, 0.0f }, + { Material.Beetroots, 0.0f }, + { Material.BirchButton, 0.0f }, + { Material.BirchLeaves, 0.0f }, + { Material.BirchLog, 0.0f }, + { Material.BirchSapling, 0.0f }, + { Material.BlackCandle, 0.0f }, + { Material.BlackCandleCake, 0.0f }, + { Material.BlackShulkerBox, 0.0f }, + { Material.BlackstoneWall, 0.0f }, + { Material.BlueCandle, 0.0f }, + { Material.BlueCandleCake, 0.0f }, + { Material.BlueOrchid, 0.0f }, + { Material.BlueShulkerBox, 0.0f }, + { Material.BrainCoral, 0.0f }, + { Material.BrainCoralFan, 0.0f }, + { Material.BrainCoralWallFan, 0.0f }, + { Material.BrickWall, 0.0f }, + { Material.BrownCandle, 0.0f }, + { Material.BrownCandleCake, 0.0f }, + { Material.BrownMushroom, 0.0f }, + { Material.BrownShulkerBox, 0.0f }, + { Material.BubbleColumn, 0.0f }, + { Material.BubbleCoral, 0.0f }, + { Material.BubbleCoralFan, 0.0f }, + { Material.BubbleCoralWallFan, 0.0f }, + { Material.Bush, 0.0f }, + { Material.CactusFlower, 0.0f }, + { Material.CalibratedSculkSensor, 0.0f }, + { Material.Candle, 0.0f }, + { Material.CandleCake, 0.0f }, + { Material.Carrots, 0.0f }, + { Material.CaveAir, 0.0f }, + { Material.CaveVines, 0.0f }, + { Material.CaveVinesPlant, 0.0f }, + { Material.CherryButton, 0.0f }, + { Material.CherryLog, 0.0f }, + { Material.CherrySapling, 0.0f }, + { Material.ChiseledCopper, 0.0f }, + { Material.ChiseledDeepslate, 0.0f }, + { Material.ChiseledTuff, 0.0f }, + { Material.ChiseledTuffBricks, 0.0f }, + { Material.ClosedEyeblossom, 0.0f }, + { Material.CobbledDeepslateSlab, 0.0f }, + { Material.CobbledDeepslateWall, 0.0f }, + { Material.CobblestoneWall, 0.0f }, + { Material.Comparator, 0.0f }, + { Material.CopperOre, 0.0f }, + { Material.CopperTorch, 0.0f }, + { Material.CopperWallTorch, 0.0f }, + { Material.Cornflower, 0.0f }, + { Material.CrackedDeepslateBricks, 0.0f }, + { Material.CrackedDeepslateTiles, 0.0f }, + { Material.CrackedPolishedBlackstoneBricks, 0.0f }, + { Material.CrimsonButton, 0.0f }, + { Material.CrimsonFungus, 0.0f }, + { Material.CrimsonRoots, 0.0f }, + { Material.CrimsonStem, 0.0f }, + { Material.CutCopper, 0.0f }, + { Material.CutCopperSlab, 0.0f }, + { Material.CutCopperStairs, 0.0f }, + { Material.CyanCandle, 0.0f }, + { Material.CyanCandleCake, 0.0f }, + { Material.CyanShulkerBox, 0.0f }, + { Material.Dandelion, 0.0f }, + { Material.DarkOakButton, 0.0f }, + { Material.DarkOakLeaves, 0.0f }, + { Material.DarkOakLog, 0.0f }, + { Material.DarkOakSapling, 0.0f }, + { Material.DeadBrainCoral, 0.0f }, + { Material.DeadBrainCoralFan, 0.0f }, + { Material.DeadBrainCoralWallFan, 0.0f }, + { Material.DeadBubbleCoral, 0.0f }, + { Material.DeadBubbleCoralFan, 0.0f }, + { Material.DeadBubbleCoralWallFan, 0.0f }, + { Material.DeadBush, 0.0f }, + { Material.DeadFireCoral, 0.0f }, + { Material.DeadFireCoralFan, 0.0f }, + { Material.DeadFireCoralWallFan, 0.0f }, + { Material.DeadHornCoral, 0.0f }, + { Material.DeadHornCoralFan, 0.0f }, + { Material.DeadHornCoralWallFan, 0.0f }, + { Material.DeadTubeCoral, 0.0f }, + { Material.DeadTubeCoralFan, 0.0f }, + { Material.DeadTubeCoralWallFan, 0.0f }, + { Material.DecoratedPot, 0.0f }, + { Material.DeepslateBrickSlab, 0.0f }, + { Material.DeepslateBrickWall, 0.0f }, + { Material.DeepslateBricks, 0.0f }, + { Material.DeepslateTileSlab, 0.0f }, + { Material.DeepslateTileWall, 0.0f }, + { Material.DeepslateTiles, 0.0f }, + { Material.DioriteSlab, 0.0f }, + { Material.DioriteWall, 0.0f }, + { Material.DriedGhast, 0.0f }, + { Material.EndRod, 0.0f }, + { Material.EndStoneBrickSlab, 0.0f }, + { Material.EndStoneBrickWall, 0.0f }, + { Material.ExposedChiseledCopper, 0.0f }, + { Material.ExposedCopper, 0.0f }, + { Material.ExposedCopperBulb, 0.0f }, + { Material.ExposedCopperChest, 0.0f }, + { Material.ExposedCopperDoor, 0.0f }, + { Material.ExposedCopperGolemStatue, 0.0f }, + { Material.ExposedCopperGrate, 0.0f }, + { Material.ExposedCopperTrapdoor, 0.0f }, + { Material.ExposedCutCopper, 0.0f }, + { Material.ExposedCutCopperSlab, 0.0f }, + { Material.ExposedCutCopperStairs, 0.0f }, + { Material.ExposedLightningRod, 0.0f }, + { Material.Fern, 0.0f }, + { Material.Fire, 0.0f }, + { Material.FireCoral, 0.0f }, + { Material.FireCoralFan, 0.0f }, + { Material.FireCoralWallFan, 0.0f }, + { Material.FireflyBush, 0.0f }, + { Material.FlowerPot, 0.0f }, + { Material.FloweringAzalea, 0.0f }, + { Material.FloweringAzaleaLeaves, 0.0f }, + { Material.Frogspawn, 0.0f }, + { Material.GildedBlackstone, 0.0f }, + { Material.GlassPane, 0.0f }, + { Material.GraniteSlab, 0.0f }, + { Material.GraniteWall, 0.0f }, + { Material.GrayCandle, 0.0f }, + { Material.GrayCandleCake, 0.0f }, + { Material.GrayShulkerBox, 0.0f }, + { Material.GreenCandle, 0.0f }, + { Material.GreenCandleCake, 0.0f }, + { Material.GreenShulkerBox, 0.0f }, + { Material.HangingRoots, 0.0f }, + { Material.HoneyBlock, 0.0f }, + { Material.HornCoral, 0.0f }, + { Material.HornCoralFan, 0.0f }, + { Material.HornCoralWallFan, 0.0f }, + { Material.InfestedChiseledStoneBricks, 0.0f }, + { Material.InfestedCobblestone, 0.0f }, + { Material.InfestedCrackedStoneBricks, 0.0f }, + { Material.InfestedDeepslate, 0.0f }, + { Material.InfestedMossyStoneBricks, 0.0f }, + { Material.InfestedStone, 0.0f }, + { Material.InfestedStoneBricks, 0.0f }, + { Material.JungleButton, 0.0f }, + { Material.JungleLeaves, 0.0f }, + { Material.JungleLog, 0.0f }, + { Material.JungleSapling, 0.0f }, + { Material.Kelp, 0.0f }, + { Material.KelpPlant, 0.0f }, + { Material.LargeAmethystBud, 0.0f }, + { Material.LargeFern, 0.0f }, + { Material.LavaCauldron, 0.0f }, + { Material.LeafLitter, 0.0f }, + { Material.LightBlueCandle, 0.0f }, + { Material.LightBlueCandleCake, 0.0f }, + { Material.LightBlueShulkerBox, 0.0f }, + { Material.LightGrayCandle, 0.0f }, + { Material.LightGrayCandleCake, 0.0f }, + { Material.LightGrayShulkerBox, 0.0f }, + { Material.Lilac, 0.0f }, + { Material.LilyOfTheValley, 0.0f }, + { Material.LilyPad, 0.0f }, + { Material.LimeCandle, 0.0f }, + { Material.LimeCandleCake, 0.0f }, + { Material.LimeShulkerBox, 0.0f }, + { Material.MagentaCandle, 0.0f }, + { Material.MagentaCandleCake, 0.0f }, + { Material.MagentaShulkerBox, 0.0f }, + { Material.MangroveButton, 0.0f }, + { Material.MangroveLeaves, 0.0f }, + { Material.MangroveLog, 0.0f }, + { Material.MangrovePropagule, 0.0f }, + { Material.MediumAmethystBud, 0.0f }, + { Material.MossyCobblestoneSlab, 0.0f }, + { Material.MossyCobblestoneWall, 0.0f }, + { Material.MossyStoneBrickSlab, 0.0f }, + { Material.MossyStoneBrickWall, 0.0f }, + { Material.Mud, 0.0f }, + { Material.MudBrickWall, 0.0f }, + { Material.NetherBrickWall, 0.0f }, + { Material.NetherSprouts, 0.0f }, + { Material.NetherWart, 0.0f }, + { Material.OakButton, 0.0f }, + { Material.OakLeaves, 0.0f }, + { Material.OakLog, 0.0f }, + { Material.OakSapling, 0.0f }, + { Material.OpenEyeblossom, 0.0f }, + { Material.OrangeCandle, 0.0f }, + { Material.OrangeCandleCake, 0.0f }, + { Material.OrangeShulkerBox, 0.0f }, + { Material.OrangeTulip, 0.0f }, + { Material.OxeyeDaisy, 0.0f }, + { Material.OxidizedChiseledCopper, 0.0f }, + { Material.OxidizedCopper, 0.0f }, + { Material.OxidizedCopperBulb, 0.0f }, + { Material.OxidizedCopperChest, 0.0f }, + { Material.OxidizedCopperDoor, 0.0f }, + { Material.OxidizedCopperGolemStatue, 0.0f }, + { Material.OxidizedCopperGrate, 0.0f }, + { Material.OxidizedCopperTrapdoor, 0.0f }, + { Material.OxidizedCutCopper, 0.0f }, + { Material.OxidizedCutCopperSlab, 0.0f }, + { Material.OxidizedCutCopperStairs, 0.0f }, + { Material.OxidizedLightningRod, 0.0f }, + { Material.PaleHangingMoss, 0.0f }, + { Material.PaleOakButton, 0.0f }, + { Material.PaleOakLog, 0.0f }, + { Material.PaleOakSapling, 0.0f }, + { Material.Peony, 0.0f }, + { Material.PinkCandle, 0.0f }, + { Material.PinkCandleCake, 0.0f }, + { Material.PinkPetals, 0.0f }, + { Material.PinkShulkerBox, 0.0f }, + { Material.PinkTulip, 0.0f }, + { Material.Piston, 0.0f }, + { Material.PitcherCrop, 0.0f }, + { Material.PitcherPlant, 0.0f }, + { Material.PolishedAndesiteSlab, 0.0f }, + { Material.PolishedBlackstoneBrickWall, 0.0f }, + { Material.PolishedBlackstoneButton, 0.0f }, + { Material.PolishedBlackstoneSlab, 0.0f }, + { Material.PolishedBlackstoneWall, 0.0f }, + { Material.PolishedDeepslate, 0.0f }, + { Material.PolishedDeepslateSlab, 0.0f }, + { Material.PolishedDeepslateWall, 0.0f }, + { Material.PolishedDioriteSlab, 0.0f }, + { Material.PolishedGraniteSlab, 0.0f }, + { Material.PolishedTuff, 0.0f }, + { Material.PolishedTuffSlab, 0.0f }, + { Material.PolishedTuffStairs, 0.0f }, + { Material.PolishedTuffWall, 0.0f }, + { Material.Poppy, 0.0f }, + { Material.Potatoes, 0.0f }, + { Material.PottedAcaciaSapling, 0.0f }, + { Material.PottedAllium, 0.0f }, + { Material.PottedAzaleaBush, 0.0f }, + { Material.PottedAzureBluet, 0.0f }, + { Material.PottedBamboo, 0.0f }, + { Material.PottedBirchSapling, 0.0f }, + { Material.PottedBlueOrchid, 0.0f }, + { Material.PottedBrownMushroom, 0.0f }, + { Material.PottedCactus, 0.0f }, + { Material.PottedCherrySapling, 0.0f }, + { Material.PottedClosedEyeblossom, 0.0f }, + { Material.PottedCornflower, 0.0f }, + { Material.PottedCrimsonFungus, 0.0f }, + { Material.PottedCrimsonRoots, 0.0f }, + { Material.PottedDandelion, 0.0f }, + { Material.PottedDarkOakSapling, 0.0f }, + { Material.PottedDeadBush, 0.0f }, + { Material.PottedFern, 0.0f }, + { Material.PottedFloweringAzaleaBush, 0.0f }, + { Material.PottedJungleSapling, 0.0f }, + { Material.PottedLilyOfTheValley, 0.0f }, + { Material.PottedMangrovePropagule, 0.0f }, + { Material.PottedOakSapling, 0.0f }, + { Material.PottedOpenEyeblossom, 0.0f }, + { Material.PottedOrangeTulip, 0.0f }, + { Material.PottedOxeyeDaisy, 0.0f }, + { Material.PottedPaleOakSapling, 0.0f }, + { Material.PottedPinkTulip, 0.0f }, + { Material.PottedPoppy, 0.0f }, + { Material.PottedRedMushroom, 0.0f }, + { Material.PottedRedTulip, 0.0f }, + { Material.PottedSpruceSapling, 0.0f }, + { Material.PottedTorchflower, 0.0f }, + { Material.PottedWarpedFungus, 0.0f }, + { Material.PottedWarpedRoots, 0.0f }, + { Material.PottedWhiteTulip, 0.0f }, + { Material.PottedWitherRose, 0.0f }, + { Material.PowderSnowCauldron, 0.0f }, + { Material.PrismarineWall, 0.0f }, + { Material.PurpleCandle, 0.0f }, + { Material.PurpleCandleCake, 0.0f }, + { Material.PurpleShulkerBox, 0.0f }, + { Material.QuartzBricks, 0.0f }, + { Material.RedCandle, 0.0f }, + { Material.RedCandleCake, 0.0f }, + { Material.RedMushroom, 0.0f }, + { Material.RedNetherBrickSlab, 0.0f }, + { Material.RedNetherBrickWall, 0.0f }, + { Material.RedSandstoneWall, 0.0f }, + { Material.RedShulkerBox, 0.0f }, + { Material.RedTulip, 0.0f }, + { Material.RedstoneTorch, 0.0f }, + { Material.RedstoneWallTorch, 0.0f }, + { Material.RedstoneWire, 0.0f }, + { Material.Repeater, 0.0f }, + { Material.ResinBlock, 0.0f }, + { Material.ResinClump, 0.0f }, + { Material.RoseBush, 0.0f }, + { Material.SandstoneWall, 0.0f }, + { Material.Scaffolding, 0.0f }, + { Material.SeaPickle, 0.0f }, + { Material.Seagrass, 0.0f }, + { Material.ShortDryGrass, 0.0f }, + { Material.ShortGrass, 0.0f }, + { Material.ShulkerBox, 0.0f }, + { Material.SlimeBlock, 0.0f }, + { Material.SmallAmethystBud, 0.0f }, + { Material.SmallDripleaf, 0.0f }, + { Material.SmoothBasalt, 0.0f }, + { Material.SmoothQuartzSlab, 0.0f }, + { Material.SmoothRedSandstoneSlab, 0.0f }, + { Material.SmoothSandstoneSlab, 0.0f }, + { Material.SoulFire, 0.0f }, + { Material.SoulTorch, 0.0f }, + { Material.SoulWallTorch, 0.0f }, + { Material.SporeBlossom, 0.0f }, + { Material.SpruceButton, 0.0f }, + { Material.SpruceLeaves, 0.0f }, + { Material.SpruceLog, 0.0f }, + { Material.SpruceSapling, 0.0f }, + { Material.StickyPiston, 0.0f }, + { Material.StoneBrickWall, 0.0f }, + { Material.StoneButton, 0.0f }, + { Material.StrippedAcaciaLog, 0.0f }, + { Material.StrippedBambooBlock, 0.0f }, + { Material.StrippedBirchLog, 0.0f }, + { Material.StrippedCherryLog, 0.0f }, + { Material.StrippedCrimsonStem, 0.0f }, + { Material.StrippedDarkOakLog, 0.0f }, + { Material.StrippedJungleLog, 0.0f }, + { Material.StrippedMangroveLog, 0.0f }, + { Material.StrippedMangroveWood, 0.0f }, + { Material.StrippedOakLog, 0.0f }, + { Material.StrippedPaleOakLog, 0.0f }, + { Material.StrippedSpruceLog, 0.0f }, + { Material.StrippedWarpedStem, 0.0f }, + { Material.StructureVoid, 0.0f }, + { Material.SugarCane, 0.0f }, + { Material.Sunflower, 0.0f }, + { Material.SweetBerryBush, 0.0f }, + { Material.TallDryGrass, 0.0f }, + { Material.TallGrass, 0.0f }, + { Material.TallSeagrass, 0.0f }, + { Material.TintedGlass, 0.0f }, + { Material.Tnt, 0.0f }, + { Material.Torch, 0.0f }, + { Material.Torchflower, 0.0f }, + { Material.TorchflowerCrop, 0.0f }, + { Material.Tripwire, 0.0f }, + { Material.TripwireHook, 0.0f }, + { Material.TubeCoral, 0.0f }, + { Material.TubeCoralFan, 0.0f }, + { Material.TubeCoralWallFan, 0.0f }, + { Material.TuffBrickSlab, 0.0f }, + { Material.TuffBrickStairs, 0.0f }, + { Material.TuffBrickWall, 0.0f }, + { Material.TuffBricks, 0.0f }, + { Material.TuffSlab, 0.0f }, + { Material.TuffStairs, 0.0f }, + { Material.TuffWall, 0.0f }, + { Material.TwistingVines, 0.0f }, + { Material.TwistingVinesPlant, 0.0f }, + { Material.VoidAir, 0.0f }, + { Material.WallTorch, 0.0f }, + { Material.WarpedButton, 0.0f }, + { Material.WarpedFungus, 0.0f }, + { Material.WarpedRoots, 0.0f }, + { Material.WarpedStem, 0.0f }, + { Material.WaterCauldron, 0.0f }, + { Material.WaxedChiseledCopper, 0.0f }, + { Material.WaxedCopperBlock, 0.0f }, + { Material.WaxedCopperBulb, 0.0f }, + { Material.WaxedCopperChest, 0.0f }, + { Material.WaxedCopperDoor, 0.0f }, + { Material.WaxedCopperGolemStatue, 0.0f }, + { Material.WaxedCopperGrate, 0.0f }, + { Material.WaxedCopperTrapdoor, 0.0f }, + { Material.WaxedCutCopper, 0.0f }, + { Material.WaxedCutCopperSlab, 0.0f }, + { Material.WaxedExposedChiseledCopper, 0.0f }, + { Material.WaxedExposedCopper, 0.0f }, + { Material.WaxedExposedCopperBulb, 0.0f }, + { Material.WaxedExposedCopperChest, 0.0f }, + { Material.WaxedExposedCopperDoor, 0.0f }, + { Material.WaxedExposedCopperGolemStatue, 0.0f }, + { Material.WaxedExposedCopperGrate, 0.0f }, + { Material.WaxedExposedCopperTrapdoor, 0.0f }, + { Material.WaxedExposedCutCopper, 0.0f }, + { Material.WaxedExposedCutCopperSlab, 0.0f }, + { Material.WaxedExposedLightningRod, 0.0f }, + { Material.WaxedLightningRod, 0.0f }, + { Material.WaxedOxidizedChiseledCopper, 0.0f }, + { Material.WaxedOxidizedCopper, 0.0f }, + { Material.WaxedOxidizedCopperBulb, 0.0f }, + { Material.WaxedOxidizedCopperChest, 0.0f }, + { Material.WaxedOxidizedCopperDoor, 0.0f }, + { Material.WaxedOxidizedCopperGolemStatue, 0.0f }, + { Material.WaxedOxidizedCopperGrate, 0.0f }, + { Material.WaxedOxidizedCopperTrapdoor, 0.0f }, + { Material.WaxedOxidizedCutCopper, 0.0f }, + { Material.WaxedOxidizedCutCopperSlab, 0.0f }, + { Material.WaxedOxidizedLightningRod, 0.0f }, + { Material.WaxedWeatheredChiseledCopper, 0.0f }, + { Material.WaxedWeatheredCopper, 0.0f }, + { Material.WaxedWeatheredCopperBulb, 0.0f }, + { Material.WaxedWeatheredCopperChest, 0.0f }, + { Material.WaxedWeatheredCopperDoor, 0.0f }, + { Material.WaxedWeatheredCopperGolemStatue, 0.0f }, + { Material.WaxedWeatheredCopperGrate, 0.0f }, + { Material.WaxedWeatheredCopperTrapdoor, 0.0f }, + { Material.WaxedWeatheredCutCopper, 0.0f }, + { Material.WaxedWeatheredCutCopperSlab, 0.0f }, + { Material.WaxedWeatheredLightningRod, 0.0f }, + { Material.WeatheredChiseledCopper, 0.0f }, + { Material.WeatheredCopper, 0.0f }, + { Material.WeatheredCopperBulb, 0.0f }, + { Material.WeatheredCopperChest, 0.0f }, + { Material.WeatheredCopperDoor, 0.0f }, + { Material.WeatheredCopperGolemStatue, 0.0f }, + { Material.WeatheredCopperGrate, 0.0f }, + { Material.WeatheredCopperTrapdoor, 0.0f }, + { Material.WeatheredCutCopper, 0.0f }, + { Material.WeatheredCutCopperSlab, 0.0f }, + { Material.WeatheredCutCopperStairs, 0.0f }, + { Material.WeatheredLightningRod, 0.0f }, + { Material.WeepingVines, 0.0f }, + { Material.WeepingVinesPlant, 0.0f }, + { Material.Wheat, 0.0f }, + { Material.WhiteCandle, 0.0f }, + { Material.WhiteCandleCake, 0.0f }, + { Material.WhiteShulkerBox, 0.0f }, + { Material.WhiteTulip, 0.0f }, + { Material.Wildflowers, 0.0f }, + { Material.WitherRose, 0.0f }, + { Material.YellowCandle, 0.0f }, + { Material.YellowCandleCake, 0.0f }, + { Material.YellowShulkerBox, 0.0f }, + // Hardness 0.1: 23 blocks + { Material.BigDripleaf, 0.1f }, + { Material.BigDripleafStem, 0.1f }, + { Material.BlackCarpet, 0.1f }, + { Material.BlueCarpet, 0.1f }, + { Material.BrownCarpet, 0.1f }, + { Material.CyanCarpet, 0.1f }, + { Material.GrayCarpet, 0.1f }, + { Material.GreenCarpet, 0.1f }, + { Material.LightBlueCarpet, 0.1f }, + { Material.LightGrayCarpet, 0.1f }, + { Material.LimeCarpet, 0.1f }, + { Material.MagentaCarpet, 0.1f }, + { Material.MossBlock, 0.1f }, + { Material.MossCarpet, 0.1f }, + { Material.OrangeCarpet, 0.1f }, + { Material.PaleMossBlock, 0.1f }, + { Material.PaleMossCarpet, 0.1f }, + { Material.PinkCarpet, 0.1f }, + { Material.PurpleCarpet, 0.1f }, + { Material.RedCarpet, 0.1f }, + { Material.Snow, 0.1f }, + { Material.WhiteCarpet, 0.1f }, + { Material.YellowCarpet, 0.1f }, + // Hardness 0.2: 12 blocks + { Material.BrownMushroomBlock, 0.2f }, + { Material.CherryLeaves, 0.2f }, + { Material.Cocoa, 0.2f }, + { Material.DaylightDetector, 0.2f }, + { Material.GlowLichen, 0.2f }, + { Material.MushroomStem, 0.2f }, + { Material.PaleOakLeaves, 0.2f }, + { Material.RedMushroomBlock, 0.2f }, + { Material.Sculk, 0.2f }, + { Material.SculkVein, 0.2f }, + { Material.SnowBlock, 0.2f }, + { Material.Vine, 0.2f }, + { Material.PowderSnow, 0.25f }, + { Material.SuspiciousGravel, 0.25f }, + { Material.SuspiciousSand, 0.25f }, + // Hardness 0.3: 24 blocks + { Material.BeeNest, 0.3f }, + { Material.BlackStainedGlassPane, 0.3f }, + { Material.BlueStainedGlassPane, 0.3f }, + { Material.BrownStainedGlassPane, 0.3f }, + { Material.CyanStainedGlassPane, 0.3f }, + { Material.Glass, 0.3f }, + { Material.Glowstone, 0.3f }, + { Material.GrayStainedGlassPane, 0.3f }, + { Material.GreenStainedGlassPane, 0.3f }, + { Material.LightBlueStainedGlassPane, 0.3f }, + { Material.LightGrayStainedGlassPane, 0.3f }, + { Material.LimeStainedGlassPane, 0.3f }, + { Material.MagentaStainedGlassPane, 0.3f }, + { Material.OchreFroglight, 0.3f }, + { Material.OrangeStainedGlassPane, 0.3f }, + { Material.PearlescentFroglight, 0.3f }, + { Material.PinkStainedGlassPane, 0.3f }, + { Material.PurpleStainedGlassPane, 0.3f }, + { Material.RedStainedGlassPane, 0.3f }, + { Material.RedstoneLamp, 0.3f }, + { Material.SeaLantern, 0.3f }, + { Material.VerdantFroglight, 0.3f }, + { Material.WhiteStainedGlassPane, 0.3f }, + { Material.YellowStainedGlassPane, 0.3f }, + { Material.Cactus, 0.4f }, + { Material.ChorusFlower, 0.4f }, + { Material.ChorusPlant, 0.4f }, + { Material.CrimsonNylium, 0.4f }, + { Material.Ladder, 0.4f }, + { Material.Netherrack, 0.4f }, + { Material.WarpedNylium, 0.4f }, + // Hardness 0.5: 52 blocks + { Material.AcaciaPressurePlate, 0.5f }, + { Material.BambooPressurePlate, 0.5f }, + { Material.BirchPressurePlate, 0.5f }, + { Material.BlackConcretePowder, 0.5f }, + { Material.BlueConcretePowder, 0.5f }, + { Material.BrewingStand, 0.5f }, + { Material.BrownConcretePowder, 0.5f }, + { Material.Cake, 0.5f }, + { Material.CherryPressurePlate, 0.5f }, + { Material.CoarseDirt, 0.5f }, + { Material.CrimsonPressurePlate, 0.5f }, + { Material.CyanConcretePowder, 0.5f }, + { Material.DarkOakPressurePlate, 0.5f }, + { Material.Dirt, 0.5f }, + { Material.DriedKelpBlock, 0.5f }, + { Material.FrostedIce, 0.5f }, + { Material.GrayConcretePowder, 0.5f }, + { Material.GreenConcretePowder, 0.5f }, + { Material.HayBlock, 0.5f }, + { Material.HeavyWeightedPressurePlate, 0.5f }, + { Material.Ice, 0.5f }, + { Material.JunglePressurePlate, 0.5f }, + { Material.Lever, 0.5f }, + { Material.LightBlueConcretePowder, 0.5f }, + { Material.LightGrayConcretePowder, 0.5f }, + { Material.LightWeightedPressurePlate, 0.5f }, + { Material.LimeConcretePowder, 0.5f }, + { Material.MagentaConcretePowder, 0.5f }, + { Material.MagmaBlock, 0.5f }, + { Material.MangrovePressurePlate, 0.5f }, + { Material.OakPressurePlate, 0.5f }, + { Material.OrangeConcretePowder, 0.5f }, + { Material.PackedIce, 0.5f }, + { Material.PaleOakPressurePlate, 0.5f }, + { Material.PinkConcretePowder, 0.5f }, + { Material.Podzol, 0.5f }, + { Material.PolishedBlackstonePressurePlate, 0.5f }, + { Material.PurpleConcretePowder, 0.5f }, + { Material.RedConcretePowder, 0.5f }, + { Material.RedSand, 0.5f }, + { Material.RootedDirt, 0.5f }, + { Material.Sand, 0.5f }, + { Material.SnifferEgg, 0.5f }, + { Material.SoulSand, 0.5f }, + { Material.SoulSoil, 0.5f }, + { Material.SprucePressurePlate, 0.5f }, + { Material.StonePressurePlate, 0.5f }, + { Material.Target, 0.5f }, + { Material.TurtleEgg, 0.5f }, + { Material.WarpedPressurePlate, 0.5f }, + { Material.WhiteConcretePowder, 0.5f }, + { Material.YellowConcretePowder, 0.5f }, + // Hardness 0.6: 10 blocks + { Material.Beehive, 0.6f }, + { Material.Clay, 0.6f }, + { Material.Composter, 0.6f }, + { Material.Farmland, 0.6f }, + { Material.GrassBlock, 0.6f }, + { Material.Gravel, 0.6f }, + { Material.HoneycombBlock, 0.6f }, + { Material.Mycelium, 0.6f }, + { Material.Sponge, 0.6f }, + { Material.WetSponge, 0.6f }, + { Material.DirtPath, 0.65f }, + { Material.ActivatorRail, 0.7f }, + { Material.DetectorRail, 0.7f }, + { Material.MangroveRoots, 0.7f }, + { Material.MuddyMangroveRoots, 0.7f }, + { Material.PoweredRail, 0.7f }, + { Material.Rail, 0.7f }, + { Material.Calcite, 0.75f }, + // Hardness 0.8: 26 blocks + { Material.BlackWool, 0.8f }, + { Material.BlueWool, 0.8f }, + { Material.BrownWool, 0.8f }, + { Material.ChiseledQuartzBlock, 0.8f }, + { Material.ChiseledRedSandstone, 0.8f }, + { Material.ChiseledSandstone, 0.8f }, + { Material.CutRedSandstone, 0.8f }, + { Material.CutSandstone, 0.8f }, + { Material.CyanWool, 0.8f }, + { Material.GrayWool, 0.8f }, + { Material.GreenWool, 0.8f }, + { Material.LightBlueWool, 0.8f }, + { Material.LightGrayWool, 0.8f }, + { Material.LimeWool, 0.8f }, + { Material.MagentaWool, 0.8f }, + { Material.NoteBlock, 0.8f }, + { Material.OrangeWool, 0.8f }, + { Material.PinkWool, 0.8f }, + { Material.PurpleWool, 0.8f }, + { Material.QuartzBlock, 0.8f }, + { Material.QuartzPillar, 0.8f }, + { Material.RedSandstone, 0.8f }, + { Material.RedWool, 0.8f }, + { Material.Sandstone, 0.8f }, + { Material.WhiteWool, 0.8f }, + { Material.YellowWool, 0.8f }, + // Hardness 1.0: 100 blocks + { Material.AcaciaHangingSign, 1.0f }, + { Material.AcaciaSign, 1.0f }, + { Material.AcaciaWallHangingSign, 1.0f }, + { Material.AcaciaWallSign, 1.0f }, + { Material.BambooHangingSign, 1.0f }, + { Material.BambooSign, 1.0f }, + { Material.BambooWallHangingSign, 1.0f }, + { Material.BambooWallSign, 1.0f }, + { Material.BirchHangingSign, 1.0f }, + { Material.BirchSign, 1.0f }, + { Material.BirchWallHangingSign, 1.0f }, + { Material.BirchWallSign, 1.0f }, + { Material.BlackBanner, 1.0f }, + { Material.BlackWallBanner, 1.0f }, + { Material.BlueBanner, 1.0f }, + { Material.BlueWallBanner, 1.0f }, + { Material.BrownBanner, 1.0f }, + { Material.BrownWallBanner, 1.0f }, + { Material.CarvedPumpkin, 1.0f }, + { Material.CherryHangingSign, 1.0f }, + { Material.CherrySign, 1.0f }, + { Material.CherryWallHangingSign, 1.0f }, + { Material.CherryWallSign, 1.0f }, + { Material.CreeperHead, 1.0f }, + { Material.CreeperWallHead, 1.0f }, + { Material.CrimsonHangingSign, 1.0f }, + { Material.CrimsonSign, 1.0f }, + { Material.CrimsonWallHangingSign, 1.0f }, + { Material.CrimsonWallSign, 1.0f }, + { Material.CyanBanner, 1.0f }, + { Material.CyanWallBanner, 1.0f }, + { Material.DarkOakHangingSign, 1.0f }, + { Material.DarkOakSign, 1.0f }, + { Material.DarkOakWallHangingSign, 1.0f }, + { Material.DarkOakWallSign, 1.0f }, + { Material.DragonHead, 1.0f }, + { Material.DragonWallHead, 1.0f }, + { Material.GrayBanner, 1.0f }, + { Material.GrayWallBanner, 1.0f }, + { Material.GreenBanner, 1.0f }, + { Material.GreenWallBanner, 1.0f }, + { Material.JackOLantern, 1.0f }, + { Material.JungleHangingSign, 1.0f }, + { Material.JungleSign, 1.0f }, + { Material.JungleWallHangingSign, 1.0f }, + { Material.JungleWallSign, 1.0f }, + { Material.LightBlueBanner, 1.0f }, + { Material.LightBlueWallBanner, 1.0f }, + { Material.LightGrayBanner, 1.0f }, + { Material.LightGrayWallBanner, 1.0f }, + { Material.LimeBanner, 1.0f }, + { Material.LimeWallBanner, 1.0f }, + { Material.MagentaBanner, 1.0f }, + { Material.MagentaWallBanner, 1.0f }, + { Material.MangroveHangingSign, 1.0f }, + { Material.MangroveSign, 1.0f }, + { Material.MangroveWallHangingSign, 1.0f }, + { Material.MangroveWallSign, 1.0f }, + { Material.NetherWartBlock, 1.0f }, + { Material.OakHangingSign, 1.0f }, + { Material.OakSign, 1.0f }, + { Material.OakWallHangingSign, 1.0f }, + { Material.OakWallSign, 1.0f }, + { Material.OrangeBanner, 1.0f }, + { Material.OrangeWallBanner, 1.0f }, + { Material.PackedMud, 1.0f }, + { Material.PaleOakHangingSign, 1.0f }, + { Material.PaleOakSign, 1.0f }, + { Material.PaleOakWallHangingSign, 1.0f }, + { Material.PaleOakWallSign, 1.0f }, + { Material.PiglinHead, 1.0f }, + { Material.PiglinWallHead, 1.0f }, + { Material.PinkBanner, 1.0f }, + { Material.PinkWallBanner, 1.0f }, + { Material.PlayerHead, 1.0f }, + { Material.PlayerWallHead, 1.0f }, + { Material.PurpleBanner, 1.0f }, + { Material.PurpleWallBanner, 1.0f }, + { Material.RedBanner, 1.0f }, + { Material.RedWallBanner, 1.0f }, + { Material.Shroomlight, 1.0f }, + { Material.SkeletonSkull, 1.0f }, + { Material.SkeletonWallSkull, 1.0f }, + { Material.SpruceHangingSign, 1.0f }, + { Material.SpruceSign, 1.0f }, + { Material.SpruceWallHangingSign, 1.0f }, + { Material.SpruceWallSign, 1.0f }, + { Material.WarpedHangingSign, 1.0f }, + { Material.WarpedSign, 1.0f }, + { Material.WarpedWallHangingSign, 1.0f }, + { Material.WarpedWallSign, 1.0f }, + { Material.WarpedWartBlock, 1.0f }, + { Material.WhiteBanner, 1.0f }, + { Material.WhiteWallBanner, 1.0f }, + { Material.WitherSkeletonSkull, 1.0f }, + { Material.WitherSkeletonWallSkull, 1.0f }, + { Material.YellowBanner, 1.0f }, + { Material.YellowWallBanner, 1.0f }, + { Material.ZombieHead, 1.0f }, + { Material.ZombieWallHead, 1.0f }, + // Hardness 1.25: 19 blocks + { Material.Basalt, 1.25f }, + { Material.BlackTerracotta, 1.25f }, + { Material.BlueTerracotta, 1.25f }, + { Material.BrownTerracotta, 1.25f }, + { Material.CyanTerracotta, 1.25f }, + { Material.GrayTerracotta, 1.25f }, + { Material.GreenTerracotta, 1.25f }, + { Material.LightBlueTerracotta, 1.25f }, + { Material.LightGrayTerracotta, 1.25f }, + { Material.LimeTerracotta, 1.25f }, + { Material.MagentaTerracotta, 1.25f }, + { Material.OrangeTerracotta, 1.25f }, + { Material.PinkTerracotta, 1.25f }, + { Material.PolishedBasalt, 1.25f }, + { Material.PurpleTerracotta, 1.25f }, + { Material.RedTerracotta, 1.25f }, + { Material.Terracotta, 1.25f }, + { Material.WhiteTerracotta, 1.25f }, + { Material.YellowTerracotta, 1.25f }, + // Hardness 1.4: 16 blocks + { Material.BlackGlazedTerracotta, 1.4f }, + { Material.BlueGlazedTerracotta, 1.4f }, + { Material.BrownGlazedTerracotta, 1.4f }, + { Material.CyanGlazedTerracotta, 1.4f }, + { Material.GrayGlazedTerracotta, 1.4f }, + { Material.GreenGlazedTerracotta, 1.4f }, + { Material.LightBlueGlazedTerracotta, 1.4f }, + { Material.LightGrayGlazedTerracotta, 1.4f }, + { Material.LimeGlazedTerracotta, 1.4f }, + { Material.MagentaGlazedTerracotta, 1.4f }, + { Material.OrangeGlazedTerracotta, 1.4f }, + { Material.PinkGlazedTerracotta, 1.4f }, + { Material.PurpleGlazedTerracotta, 1.4f }, + { Material.RedGlazedTerracotta, 1.4f }, + { Material.WhiteGlazedTerracotta, 1.4f }, + { Material.YellowGlazedTerracotta, 1.4f }, + // Hardness 1.5: 49 blocks + { Material.AmethystBlock, 1.5f }, + { Material.AmethystCluster, 1.5f }, + { Material.Andesite, 1.5f }, + { Material.Blackstone, 1.5f }, + { Material.Bookshelf, 1.5f }, + { Material.BrainCoralBlock, 1.5f }, + { Material.BubbleCoralBlock, 1.5f }, + { Material.BuddingAmethyst, 1.5f }, + { Material.ChiseledBookshelf, 1.5f }, + { Material.ChiseledPolishedBlackstone, 1.5f }, + { Material.ChiseledResinBricks, 1.5f }, + { Material.ChiseledStoneBricks, 1.5f }, + { Material.CrackedStoneBricks, 1.5f }, + { Material.Crafter, 1.5f }, + { Material.DarkPrismarine, 1.5f }, + { Material.DarkPrismarineSlab, 1.5f }, + { Material.DeadBrainCoralBlock, 1.5f }, + { Material.DeadBubbleCoralBlock, 1.5f }, + { Material.DeadFireCoralBlock, 1.5f }, + { Material.DeadHornCoralBlock, 1.5f }, + { Material.DeadTubeCoralBlock, 1.5f }, + { Material.Diorite, 1.5f }, + { Material.DripstoneBlock, 1.5f }, + { Material.FireCoralBlock, 1.5f }, + { Material.Granite, 1.5f }, + { Material.HornCoralBlock, 1.5f }, + { Material.MossyStoneBricks, 1.5f }, + { Material.MudBrickSlab, 1.5f }, + { Material.MudBricks, 1.5f }, + { Material.PistonHead, 1.5f }, + { Material.PointedDripstone, 1.5f }, + { Material.PolishedAndesite, 1.5f }, + { Material.PolishedBlackstoneBricks, 1.5f }, + { Material.PolishedDiorite, 1.5f }, + { Material.PolishedGranite, 1.5f }, + { Material.Prismarine, 1.5f }, + { Material.PrismarineBrickSlab, 1.5f }, + { Material.PrismarineBricks, 1.5f }, + { Material.PrismarineSlab, 1.5f }, + { Material.PurpurBlock, 1.5f }, + { Material.PurpurPillar, 1.5f }, + { Material.ResinBrickSlab, 1.5f }, + { Material.ResinBrickWall, 1.5f }, + { Material.ResinBricks, 1.5f }, + { Material.SculkSensor, 1.5f }, + { Material.Stone, 1.5f }, + { Material.StoneBricks, 1.5f }, + { Material.TubeCoralBlock, 1.5f }, + { Material.Tuff, 1.5f }, + // Hardness 1.8: 16 blocks + { Material.BlackConcrete, 1.8f }, + { Material.BlueConcrete, 1.8f }, + { Material.BrownConcrete, 1.8f }, + { Material.CyanConcrete, 1.8f }, + { Material.GrayConcrete, 1.8f }, + { Material.GreenConcrete, 1.8f }, + { Material.LightBlueConcrete, 1.8f }, + { Material.LightGrayConcrete, 1.8f }, + { Material.LimeConcrete, 1.8f }, + { Material.MagentaConcrete, 1.8f }, + { Material.OrangeConcrete, 1.8f }, + { Material.PinkConcrete, 1.8f }, + { Material.PurpleConcrete, 1.8f }, + { Material.RedConcrete, 1.8f }, + { Material.WhiteConcrete, 1.8f }, + { Material.YellowConcrete, 1.8f }, + // Hardness 2.0: 117 blocks + { Material.AcaciaFence, 2.0f }, + { Material.AcaciaFenceGate, 2.0f }, + { Material.AcaciaPlanks, 2.0f }, + { Material.AcaciaShelf, 2.0f }, + { Material.AcaciaSlab, 2.0f }, + { Material.AcaciaWood, 2.0f }, + { Material.BambooFence, 2.0f }, + { Material.BambooFenceGate, 2.0f }, + { Material.BambooMosaic, 2.0f }, + { Material.BambooMosaicSlab, 2.0f }, + { Material.BambooPlanks, 2.0f }, + { Material.BambooShelf, 2.0f }, + { Material.BambooSlab, 2.0f }, + { Material.BirchFence, 2.0f }, + { Material.BirchFenceGate, 2.0f }, + { Material.BirchPlanks, 2.0f }, + { Material.BirchShelf, 2.0f }, + { Material.BirchSlab, 2.0f }, + { Material.BirchWood, 2.0f }, + { Material.BlackstoneSlab, 2.0f }, + { Material.BoneBlock, 2.0f }, + { Material.BrickSlab, 2.0f }, + { Material.Bricks, 2.0f }, + { Material.Campfire, 2.0f }, + { Material.Cauldron, 2.0f }, + { Material.CherryFence, 2.0f }, + { Material.CherryFenceGate, 2.0f }, + { Material.CherryPlanks, 2.0f }, + { Material.CherryShelf, 2.0f }, + { Material.CherrySlab, 2.0f }, + { Material.CherryWood, 2.0f }, + { Material.ChiseledNetherBricks, 2.0f }, + { Material.Cobblestone, 2.0f }, + { Material.CobblestoneSlab, 2.0f }, + { Material.CrackedNetherBricks, 2.0f }, + { Material.CrimsonFence, 2.0f }, + { Material.CrimsonFenceGate, 2.0f }, + { Material.CrimsonHyphae, 2.0f }, + { Material.CrimsonPlanks, 2.0f }, + { Material.CrimsonShelf, 2.0f }, + { Material.CrimsonSlab, 2.0f }, + { Material.CutRedSandstoneSlab, 2.0f }, + { Material.CutSandstoneSlab, 2.0f }, + { Material.DarkOakFence, 2.0f }, + { Material.DarkOakFenceGate, 2.0f }, + { Material.DarkOakPlanks, 2.0f }, + { Material.DarkOakShelf, 2.0f }, + { Material.DarkOakSlab, 2.0f }, + { Material.DarkOakWood, 2.0f }, + { Material.Grindstone, 2.0f }, + { Material.Jukebox, 2.0f }, + { Material.JungleFence, 2.0f }, + { Material.JungleFenceGate, 2.0f }, + { Material.JunglePlanks, 2.0f }, + { Material.JungleShelf, 2.0f }, + { Material.JungleSlab, 2.0f }, + { Material.JungleWood, 2.0f }, + { Material.MangroveFence, 2.0f }, + { Material.MangroveFenceGate, 2.0f }, + { Material.MangrovePlanks, 2.0f }, + { Material.MangroveShelf, 2.0f }, + { Material.MangroveSlab, 2.0f }, + { Material.MangroveWood, 2.0f }, + { Material.MossyCobblestone, 2.0f }, + { Material.NetherBrickFence, 2.0f }, + { Material.NetherBrickSlab, 2.0f }, + { Material.NetherBricks, 2.0f }, + { Material.OakFence, 2.0f }, + { Material.OakFenceGate, 2.0f }, + { Material.OakPlanks, 2.0f }, + { Material.OakShelf, 2.0f }, + { Material.OakSlab, 2.0f }, + { Material.OakWood, 2.0f }, + { Material.PaleOakFence, 2.0f }, + { Material.PaleOakFenceGate, 2.0f }, + { Material.PaleOakPlanks, 2.0f }, + { Material.PaleOakShelf, 2.0f }, + { Material.PaleOakSlab, 2.0f }, + { Material.PaleOakWood, 2.0f }, + { Material.PetrifiedOakSlab, 2.0f }, + { Material.PolishedBlackstone, 2.0f }, + { Material.PolishedBlackstoneBrickSlab, 2.0f }, + { Material.PurpurSlab, 2.0f }, + { Material.QuartzSlab, 2.0f }, + { Material.RedNetherBricks, 2.0f }, + { Material.RedSandstoneSlab, 2.0f }, + { Material.SandstoneSlab, 2.0f }, + { Material.SmoothQuartz, 2.0f }, + { Material.SmoothRedSandstone, 2.0f }, + { Material.SmoothSandstone, 2.0f }, + { Material.SmoothStone, 2.0f }, + { Material.SmoothStoneSlab, 2.0f }, + { Material.SoulCampfire, 2.0f }, + { Material.SpruceFence, 2.0f }, + { Material.SpruceFenceGate, 2.0f }, + { Material.SprucePlanks, 2.0f }, + { Material.SpruceShelf, 2.0f }, + { Material.SpruceSlab, 2.0f }, + { Material.SpruceWood, 2.0f }, + { Material.StoneBrickSlab, 2.0f }, + { Material.StoneSlab, 2.0f }, + { Material.StrippedAcaciaWood, 2.0f }, + { Material.StrippedBirchWood, 2.0f }, + { Material.StrippedCherryWood, 2.0f }, + { Material.StrippedCrimsonHyphae, 2.0f }, + { Material.StrippedDarkOakWood, 2.0f }, + { Material.StrippedJungleWood, 2.0f }, + { Material.StrippedOakWood, 2.0f }, + { Material.StrippedPaleOakWood, 2.0f }, + { Material.StrippedSpruceWood, 2.0f }, + { Material.StrippedWarpedHyphae, 2.0f }, + { Material.WarpedFence, 2.0f }, + { Material.WarpedFenceGate, 2.0f }, + { Material.WarpedHyphae, 2.0f }, + { Material.WarpedPlanks, 2.0f }, + { Material.WarpedShelf, 2.0f }, + { Material.WarpedSlab, 2.0f }, + // Hardness 2.5: 9 blocks + { Material.Barrel, 2.5f }, + { Material.CartographyTable, 2.5f }, + { Material.Chest, 2.5f }, + { Material.CraftingTable, 2.5f }, + { Material.FletchingTable, 2.5f }, + { Material.Lectern, 2.5f }, + { Material.Loom, 2.5f }, + { Material.SmithingTable, 2.5f }, + { Material.TrappedChest, 2.5f }, + { Material.BlueIce, 2.8f }, + // Hardness 3.0: 53 blocks + { Material.AcaciaDoor, 3.0f }, + { Material.AcaciaTrapdoor, 3.0f }, + { Material.BambooDoor, 3.0f }, + { Material.BambooTrapdoor, 3.0f }, + { Material.Beacon, 3.0f }, + { Material.BirchDoor, 3.0f }, + { Material.BirchTrapdoor, 3.0f }, + { Material.CherryDoor, 3.0f }, + { Material.CherryTrapdoor, 3.0f }, + { Material.CoalOre, 3.0f }, + { Material.Conduit, 3.0f }, + { Material.CopperBlock, 3.0f }, + { Material.CopperBulb, 3.0f }, + { Material.CopperChest, 3.0f }, + { Material.CopperDoor, 3.0f }, + { Material.CopperGolemStatue, 3.0f }, + { Material.CopperGrate, 3.0f }, + { Material.CopperTrapdoor, 3.0f }, + { Material.CrimsonDoor, 3.0f }, + { Material.CrimsonTrapdoor, 3.0f }, + { Material.DarkOakDoor, 3.0f }, + { Material.DarkOakTrapdoor, 3.0f }, + { Material.Deepslate, 3.0f }, + { Material.DiamondOre, 3.0f }, + { Material.DragonEgg, 3.0f }, + { Material.EmeraldOre, 3.0f }, + { Material.EndStone, 3.0f }, + { Material.EndStoneBricks, 3.0f }, + { Material.GoldBlock, 3.0f }, + { Material.GoldOre, 3.0f }, + { Material.Hopper, 3.0f }, + { Material.IronOre, 3.0f }, + { Material.JungleDoor, 3.0f }, + { Material.JungleTrapdoor, 3.0f }, + { Material.LapisBlock, 3.0f }, + { Material.LapisOre, 3.0f }, + { Material.LightningRod, 3.0f }, + { Material.MangroveDoor, 3.0f }, + { Material.MangroveTrapdoor, 3.0f }, + { Material.NetherGoldOre, 3.0f }, + { Material.NetherQuartzOre, 3.0f }, + { Material.OakDoor, 3.0f }, + { Material.OakTrapdoor, 3.0f }, + { Material.Observer, 3.0f }, + { Material.PaleOakDoor, 3.0f }, + { Material.PaleOakTrapdoor, 3.0f }, + { Material.RedstoneOre, 3.0f }, + { Material.SculkCatalyst, 3.0f }, + { Material.SculkShrieker, 3.0f }, + { Material.SpruceDoor, 3.0f }, + { Material.SpruceTrapdoor, 3.0f }, + { Material.WarpedDoor, 3.0f }, + { Material.WarpedTrapdoor, 3.0f }, + // Hardness 3.5: 10 blocks + { Material.BlastFurnace, 3.5f }, + { Material.CobbledDeepslate, 3.5f }, + { Material.Dispenser, 3.5f }, + { Material.Dropper, 3.5f }, + { Material.Furnace, 3.5f }, + { Material.Lantern, 3.5f }, + { Material.Lodestone, 3.5f }, + { Material.Smoker, 3.5f }, + { Material.SoulLantern, 3.5f }, + { Material.Stonecutter, 3.5f }, + { Material.Cobweb, 4.0f }, + { Material.DeepslateCoalOre, 4.5f }, + { Material.DeepslateCopperOre, 4.5f }, + { Material.DeepslateDiamondOre, 4.5f }, + { Material.DeepslateEmeraldOre, 4.5f }, + { Material.DeepslateGoldOre, 4.5f }, + { Material.DeepslateIronOre, 4.5f }, + { Material.DeepslateLapisOre, 4.5f }, + { Material.DeepslateRedstoneOre, 4.5f }, + // Hardness 5.0: 18 blocks + { Material.Anvil, 5.0f }, + { Material.Bell, 5.0f }, + { Material.ChippedAnvil, 5.0f }, + { Material.CoalBlock, 5.0f }, + { Material.DamagedAnvil, 5.0f }, + { Material.DiamondBlock, 5.0f }, + { Material.EmeraldBlock, 5.0f }, + { Material.EnchantingTable, 5.0f }, + { Material.IronBars, 5.0f }, + { Material.IronBlock, 5.0f }, + { Material.IronChain, 5.0f }, + { Material.IronDoor, 5.0f }, + { Material.IronTrapdoor, 5.0f }, + { Material.RawCopperBlock, 5.0f }, + { Material.RawGoldBlock, 5.0f }, + { Material.RawIronBlock, 5.0f }, + { Material.RedstoneBlock, 5.0f }, + { Material.Spawner, 5.0f }, + { Material.CreakingHeart, 10.0f }, + { Material.HeavyCore, 10.0f }, + { Material.EnderChest, 22.5f }, + { Material.AncientDebris, 30.0f }, + { Material.CryingObsidian, 50.0f }, + { Material.NetheriteBlock, 50.0f }, + { Material.Obsidian, 50.0f }, + { Material.RespawnAnchor, 50.0f }, + { Material.TrialSpawner, 50.0f }, + { Material.Vault, 50.0f }, + { Material.ReinforcedDeepslate, 55.0f }, + { Material.Lava, 100.0f }, + { Material.Water, 100.0f }, + }.ToFrozenDictionary(); + + private static readonly FrozenSet RequiresCorrectToolSet = new HashSet + { + Material.AmethystBlock, + Material.AncientDebris, + Material.Andesite, + Material.Anvil, + Material.Basalt, + Material.BlackConcrete, + Material.BlackGlazedTerracotta, + Material.BlackTerracotta, + Material.Blackstone, + Material.BlastFurnace, + Material.BlueConcrete, + Material.BlueGlazedTerracotta, + Material.BlueTerracotta, + Material.BoneBlock, + Material.BrainCoralBlock, + Material.BrickSlab, + Material.Bricks, + Material.BrownConcrete, + Material.BrownGlazedTerracotta, + Material.BrownTerracotta, + Material.BubbleCoralBlock, + Material.BuddingAmethyst, + Material.Calcite, + Material.Cauldron, + Material.ChainCommandBlock, + Material.ChippedAnvil, + Material.ChiseledNetherBricks, + Material.ChiseledQuartzBlock, + Material.ChiseledRedSandstone, + Material.ChiseledResinBricks, + Material.ChiseledSandstone, + Material.ChiseledStoneBricks, + Material.CoalBlock, + Material.CoalOre, + Material.Cobblestone, + Material.CobblestoneSlab, + Material.Cobweb, + Material.CommandBlock, + Material.CopperBlock, + Material.CopperBulb, + Material.CopperChest, + Material.CopperGrate, + Material.CopperTrapdoor, + Material.CrackedNetherBricks, + Material.CrackedStoneBricks, + Material.CrimsonNylium, + Material.CryingObsidian, + Material.CutRedSandstone, + Material.CutRedSandstoneSlab, + Material.CutSandstone, + Material.CutSandstoneSlab, + Material.CyanConcrete, + Material.CyanGlazedTerracotta, + Material.CyanTerracotta, + Material.DamagedAnvil, + Material.DarkPrismarine, + Material.DarkPrismarineSlab, + Material.DeadBrainCoral, + Material.DeadBrainCoralBlock, + Material.DeadBrainCoralFan, + Material.DeadBrainCoralWallFan, + Material.DeadBubbleCoral, + Material.DeadBubbleCoralBlock, + Material.DeadBubbleCoralFan, + Material.DeadBubbleCoralWallFan, + Material.DeadFireCoral, + Material.DeadFireCoralBlock, + Material.DeadFireCoralFan, + Material.DeadFireCoralWallFan, + Material.DeadHornCoral, + Material.DeadHornCoralBlock, + Material.DeadHornCoralFan, + Material.DeadHornCoralWallFan, + Material.DeadTubeCoral, + Material.DeadTubeCoralBlock, + Material.DeadTubeCoralFan, + Material.DeadTubeCoralWallFan, + Material.Deepslate, + Material.DiamondBlock, + Material.DiamondOre, + Material.Diorite, + Material.Dispenser, + Material.DripstoneBlock, + Material.Dropper, + Material.EmeraldBlock, + Material.EmeraldOre, + Material.EnchantingTable, + Material.EndStone, + Material.EndStoneBricks, + Material.FireCoralBlock, + Material.Furnace, + Material.GoldBlock, + Material.GoldOre, + Material.Granite, + Material.GrayConcrete, + Material.GrayGlazedTerracotta, + Material.GrayTerracotta, + Material.GreenConcrete, + Material.GreenGlazedTerracotta, + Material.GreenTerracotta, + Material.Grindstone, + Material.Hopper, + Material.HornCoralBlock, + Material.IronBars, + Material.IronBlock, + Material.IronChain, + Material.IronOre, + Material.IronTrapdoor, + Material.Jigsaw, + Material.LapisBlock, + Material.LapisOre, + Material.LightBlueConcrete, + Material.LightBlueGlazedTerracotta, + Material.LightBlueTerracotta, + Material.LightGrayConcrete, + Material.LightGrayGlazedTerracotta, + Material.LightGrayTerracotta, + Material.LightningRod, + Material.LimeConcrete, + Material.LimeGlazedTerracotta, + Material.LimeTerracotta, + Material.Lodestone, + Material.MagentaConcrete, + Material.MagentaGlazedTerracotta, + Material.MagentaTerracotta, + Material.MagmaBlock, + Material.MossyCobblestone, + Material.MossyStoneBricks, + Material.MudBrickSlab, + Material.MudBricks, + Material.NetherBrickFence, + Material.NetherBrickSlab, + Material.NetherBricks, + Material.NetherGoldOre, + Material.NetherQuartzOre, + Material.NetheriteBlock, + Material.Netherrack, + Material.Observer, + Material.Obsidian, + Material.OrangeConcrete, + Material.OrangeGlazedTerracotta, + Material.OrangeTerracotta, + Material.PetrifiedOakSlab, + Material.PinkConcrete, + Material.PinkGlazedTerracotta, + Material.PinkTerracotta, + Material.PolishedAndesite, + Material.PolishedBasalt, + Material.PolishedDiorite, + Material.PolishedGranite, + Material.Prismarine, + Material.PrismarineBrickSlab, + Material.PrismarineBricks, + Material.PrismarineSlab, + Material.PurpleConcrete, + Material.PurpleGlazedTerracotta, + Material.PurpleTerracotta, + Material.PurpurBlock, + Material.PurpurPillar, + Material.PurpurSlab, + Material.QuartzBlock, + Material.QuartzPillar, + Material.QuartzSlab, + Material.RawCopperBlock, + Material.RawGoldBlock, + Material.RawIronBlock, + Material.RedConcrete, + Material.RedGlazedTerracotta, + Material.RedNetherBricks, + Material.RedSandstone, + Material.RedSandstoneSlab, + Material.RedTerracotta, + Material.RedstoneBlock, + Material.RedstoneOre, + Material.RepeatingCommandBlock, + Material.ResinBrickSlab, + Material.ResinBrickWall, + Material.ResinBricks, + Material.RespawnAnchor, + Material.Sandstone, + Material.SandstoneSlab, + Material.Smoker, + Material.SmoothQuartz, + Material.SmoothRedSandstone, + Material.SmoothSandstone, + Material.SmoothStone, + Material.SmoothStoneSlab, + Material.Snow, + Material.SnowBlock, + Material.Spawner, + Material.Stone, + Material.StoneBrickSlab, + Material.StoneBricks, + Material.StoneSlab, + Material.Stonecutter, + Material.StructureBlock, + Material.Terracotta, + Material.TubeCoralBlock, + Material.Tuff, + Material.WarpedNylium, + Material.WaxedCutCopperSlab, + Material.WaxedExposedCutCopperSlab, + Material.WaxedOxidizedCutCopperSlab, + Material.WaxedWeatheredCutCopperSlab, + Material.WhiteConcrete, + Material.WhiteGlazedTerracotta, + Material.WhiteTerracotta, + Material.YellowConcrete, + Material.YellowGlazedTerracotta, + Material.YellowTerracotta, + }.ToFrozenSet(); + } +} diff --git a/MinecraftClient/Mapping/MiningCalculator.cs b/MinecraftClient/Mapping/MiningCalculator.cs new file mode 100644 index 00000000..38cf6d18 --- /dev/null +++ b/MinecraftClient/Mapping/MiningCalculator.cs @@ -0,0 +1,493 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using MinecraftClient.Inventory; +using MinecraftClient.Protocol.Handlers; +using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6; + +namespace MinecraftClient.Mapping +{ + /// + /// Computes dig duration in ticks for survival-style block breaking. + /// Version-aware across 1.8-1.21.11+, using tool speed, enchantments, effects, and attributes. + /// + public static class MiningCalculator + { + /// + /// Compute the number of ticks required to break a block in survival mode. + /// Returns 0 for instant-break blocks, -1 for unbreakable blocks. + /// + /// The block material to break + /// The item in the player's main hand (null for empty hand) + /// The item in the player's helmet slot (null if empty, used for Aqua Affinity) + /// Currently active player effects + /// Cached player attribute values (from OnEntityProperties) + /// Whether the player's eyes are submerged in water + /// Whether the player is on the ground + /// The Minecraft protocol version + /// Ticks to break the block, 0 for instant, -1 for unbreakable + public static int ComputeDigTicks( + Material blockMaterial, + Item? heldItem, + Item? helmetItem, + Dictionary effects, + Dictionary playerAttributes, + bool isUnderwater, + bool isOnGround, + int protocolVersion) + { + float hardness = BlockHardness.GetHardness(blockMaterial); + + if (hardness < 0) + return -1; // Unbreakable + + if (hardness == 0) + return 0; // Instant break + + float destroySpeed = GetDestroySpeed( + blockMaterial, heldItem, helmetItem, effects, playerAttributes, + isUnderwater, isOnGround, protocolVersion); + + bool correctTool = HasCorrectToolForDrops(blockMaterial, heldItem, protocolVersion); + int divisor = correctTool ? 30 : 100; + + float destroyProgress = destroySpeed / hardness / divisor; + + if (destroyProgress >= 1.0f) + return 0; // Instant break + + return (int)MathF.Ceiling(1.0f / destroyProgress); + } + + /// + /// Compute the player's destroy speed for a given block, following vanilla formulas. + /// + private static float GetDestroySpeed( + Material blockMaterial, + Item? heldItem, + Item? helmetItem, + Dictionary effects, + Dictionary playerAttributes, + bool isUnderwater, + bool isOnGround, + int protocolVersion) + { + float speed = GetToolSpeed(blockMaterial, heldItem, protocolVersion); + + if (protocolVersion >= Protocol18Handler.MC_1_21_11_Version) + { + // 1.21.11+: Efficiency is delivered via the MINING_EFFICIENCY attribute + if (speed > 1.0f && playerAttributes.TryGetValue("player.mining_efficiency", out double miningEff)) + speed += (float)miningEff; + } + else + { + // Pre-1.21.11: Efficiency enchantment adds level^2 + 1 + int effLevel = GetEnchantmentLevel(heldItem, Enchantments.Efficiency, protocolVersion); + if (speed > 1.0f && effLevel > 0) + speed += effLevel * effLevel + 1; + } + + // Haste effect: multiply by 1 + 0.2 * (amplifier + 1) + if (effects.TryGetValue(Effects.Haste, out var hasteData)) + speed *= 1.0f + (hasteData.Amplifier + 1) * 0.2f; + + // Conduit Power also grants dig speed equivalent when in water + if (effects.TryGetValue(Effects.ConduitPower, out var conduitData)) + speed *= 1.0f + (conduitData.Amplifier + 1) * 0.2f; + + // Mining Fatigue + if (effects.TryGetValue(Effects.MiningFatigue, out var fatigueData)) + { + float multiplier = fatigueData.Amplifier switch + { + 0 => 0.3f, + 1 => 0.09f, + 2 => 0.0027f, + _ => 8.1E-4f + }; + speed *= multiplier; + } + + // Attribute multipliers for modern versions + if (protocolVersion >= Protocol18Handler.MC_1_20_6_Version) + { + // BLOCK_BREAK_SPEED attribute (default 1.0) + if (playerAttributes.TryGetValue("player.block_break_speed", out double bbs)) + speed *= (float)bbs; + } + + // Underwater penalty + if (isUnderwater) + { + if (protocolVersion >= Protocol18Handler.MC_1_21_11_Version) + { + // 1.21.11+: Uses SUBMERGED_MINING_SPEED attribute (default 0.2) + double submergedSpeed = 0.2; + if (playerAttributes.TryGetValue("player.submerged_mining_speed", out double sms)) + submergedSpeed = sms; + speed *= (float)submergedSpeed; + } + else + { + // Pre-1.21.11: /5 unless Aqua Affinity + bool hasAquaAffinity = GetEnchantmentLevel(helmetItem, Enchantments.AquaAffinity, protocolVersion) > 0; + if (!hasAquaAffinity) + speed /= 5.0f; + } + } + + // Airborne penalty + if (!isOnGround) + speed /= 5.0f; + + return speed; + } + + /// + /// Get the base tool mining speed for a block. + /// For 1.20.6+ with ToolComponent, uses structured component data. + /// For older versions, uses hardcoded tool speed tables. + /// + private static float GetToolSpeed(Material blockMaterial, Item? heldItem, int protocolVersion) + { + if (heldItem is null) + return 1.0f; + + // Modern path: use ToolComponent from structured components + if (protocolVersion >= Protocol18Handler.MC_1_20_6_Version) + { + var toolComp = heldItem.Components?.OfType().FirstOrDefault(); + if (toolComp is not null) + { + // Check rules for matching blocks + foreach (var rule in toolComp.Rules) + { + if (rule.HasSpeed && MatchesBlockSet(rule.Blocks, blockMaterial)) + return rule.Speed; + } + return toolComp.DefaultMiningSpeed; + } + } + + // Legacy path: hardcoded tool speed tables + return GetLegacyToolSpeed(heldItem.Type, blockMaterial); + } + + /// + /// Check whether the tool provides correct drops for a block. + /// + private static bool HasCorrectToolForDrops(Material blockMaterial, Item? heldItem, int protocolVersion) + { + if (!BlockHardness.RequiresCorrectTool(blockMaterial)) + return true; + + if (heldItem is null) + return false; + + // Modern path: check ToolComponent rules + if (protocolVersion >= Protocol18Handler.MC_1_20_6_Version) + { + var toolComp = heldItem.Components?.OfType().FirstOrDefault(); + if (toolComp is not null) + { + foreach (var rule in toolComp.Rules) + { + if (rule.HasCorrectDropForBlocks && rule.CorrectDropForBlocks + && MatchesBlockSet(rule.Blocks, blockMaterial)) + return true; + } + } + return false; + } + + // Legacy path: check if Material2Tool recommends this tool type + return IsCorrectToolLegacy(heldItem.Type, blockMaterial); + } + + /// + /// Match a block material against a ToolComponent BlockSetSubcomponent. + /// + private static bool MatchesBlockSet( + Protocol.Handlers.StructuredComponents.Components.Subcomponents._1_20_6.BlockSetSubcomponent blockSet, + Material blockMaterial) + { + if (blockSet.BlockIds is not null) + { + // Check against explicit block state IDs + foreach (int blockId in blockSet.BlockIds) + { + if (Block.Palette.FromId(blockId) == blockMaterial) + return true; + } + } + + if (blockSet.TagName is not null) + { + // Match against tag name (e.g., "minecraft:mineable/pickaxe") + return MatchesBlockTag(blockSet.TagName, blockMaterial); + } + + return false; + } + + /// + /// Approximate block tag matching using Material2Tool categories. + /// Tags like "minecraft:mineable/pickaxe" map to the appropriate tool categories. + /// + private static bool MatchesBlockTag(string tagName, Material blockMaterial) + { + // Normalize tag name + string tag = tagName.Replace("minecraft:", ""); + + ItemType[] tools = Material2Tool.GetCorrectToolForBlock(blockMaterial); + if (tools.Length == 0) + return false; + + ItemType firstTool = tools[0]; + return tag switch + { + "mineable/pickaxe" => IsPickaxe(firstTool), + "mineable/axe" => IsAxe(firstTool), + "mineable/shovel" => IsShovel(firstTool), + "mineable/hoe" => IsHoe(firstTool), + _ => false + }; + } + + /// + /// Get the enchantment level from an item, supporting both legacy NBT and modern structured components. + /// + public static int GetEnchantmentLevel(Item? item, Enchantments enchantment, int protocolVersion) + { + if (item is null) + return 0; + + // Modern path: structured components (1.20.6+) + var enchList = item.EnchantmentList; + if (enchList is not null) + { + var ench = enchList.FirstOrDefault(e => e.Type == enchantment); + if (ench is not null) + return ench.Level; + } + + // Legacy path: NBT data + if (item.NBT is not null && + item.NBT.TryGetValue("Enchantments", out object? enchantments)) + { + try + { + string enchNameLower = GetEnchantmentResourceName(enchantment); + foreach (Dictionary enchEntry in (object[])enchantments) + { + string id = ((string)enchEntry["id"]).ToLowerInvariant(); + if (id == enchNameLower || id == "minecraft:" + enchNameLower) + return (short)enchEntry["lvl"]; + } + } + catch + { + // NBT parsing failure - return 0 + } + } + + return 0; + } + + /// + /// Map Enchantments enum to Minecraft resource name (e.g., "efficiency"). + /// + private static string GetEnchantmentResourceName(Enchantments enchantment) + { + return enchantment switch + { + Enchantments.AquaAffinity => "aqua_affinity", + Enchantments.BaneOfArthropods => "bane_of_arthropods", + Enchantments.BlastProtection => "blast_protection", + Enchantments.Efficiency => "efficiency", + Enchantments.FeatherFalling => "feather_falling", + Enchantments.FireAspect => "fire_aspect", + Enchantments.FireProtection => "fire_protection", + Enchantments.FrostWalker => "frost_walker", + Enchantments.LuckOfTheSea => "luck_of_the_sea", + Enchantments.ProjectileProtection => "projectile_protection", + Enchantments.QuickCharge => "quick_charge", + Enchantments.SilkTouch => "silk_touch", + Enchantments.SoulSpeed => "soul_speed", + Enchantments.SwiftSneak => "swift_sneak", + Enchantments.VanishingCurse => "vanishing_curse", + Enchantments.BindingCurse => "binding_curse", + Enchantments.WindBurst => "wind_burst", + _ => enchantment.ToString().ToUnderscoreCase() + }; + } + + #region Legacy Tool Speed Tables + + /// + /// Legacy tool speed for pre-1.20.6 versions using hardcoded values. + /// + private static float GetLegacyToolSpeed(ItemType toolType, Material blockMaterial) + { + ItemType[] recommended = Material2Tool.GetCorrectToolForBlock(blockMaterial); + if (recommended.Length == 0) + return 1.0f; + + // Check if the held tool matches the recommended tool category + ToolCategory heldCategory = GetToolCategory(toolType); + ToolCategory neededCategory = GetToolCategory(recommended[0]); + + if (heldCategory == ToolCategory.None || heldCategory != neededCategory) + { + // Special cases: sword on cobweb, shears on specific blocks + if (toolType is ItemType.Shears && IsShearable(blockMaterial)) + return 1.5f; + if (IsSword(toolType) && blockMaterial == Material.Cobweb) + return 15.0f; + return 1.0f; + } + + return GetBaseToolSpeed(toolType); + } + + private static float GetBaseToolSpeed(ItemType toolType) + { + return toolType switch + { + // Wooden tools + ItemType.WoodenPickaxe or ItemType.WoodenAxe or ItemType.WoodenShovel or + ItemType.WoodenSword or ItemType.WoodenHoe => 2.0f, + + // Stone tools + ItemType.StonePickaxe or ItemType.StoneAxe or ItemType.StoneShovel or + ItemType.StoneSword or ItemType.StoneHoe => 4.0f, + + // Iron tools + ItemType.IronPickaxe or ItemType.IronAxe or ItemType.IronShovel or + ItemType.IronSword or ItemType.IronHoe => 6.0f, + + // Diamond tools + ItemType.DiamondPickaxe or ItemType.DiamondAxe or ItemType.DiamondShovel or + ItemType.DiamondSword or ItemType.DiamondHoe => 8.0f, + + // Netherite tools + ItemType.NetheritePickaxe or ItemType.NetheriteAxe or ItemType.NetheriteShovel or + ItemType.NetheriteSword or ItemType.NetheriteHoe => 9.0f, + + // Golden tools + ItemType.GoldenPickaxe or ItemType.GoldenAxe or ItemType.GoldenShovel or + ItemType.GoldenSword or ItemType.GoldenHoe => 12.0f, + + // Shears + ItemType.Shears => 2.0f, + + _ => 1.0f + }; + } + + /// + /// Check if the held tool is the correct tool for drops in legacy versions. + /// Uses Material2Tool's recommendations to determine correctness. + /// + private static bool IsCorrectToolLegacy(ItemType toolType, Material blockMaterial) + { + ItemType[] recommended = Material2Tool.GetCorrectToolForBlock(blockMaterial); + if (recommended.Length == 0) + return false; + + ToolCategory heldCategory = GetToolCategory(toolType); + ToolCategory neededCategory = GetToolCategory(recommended[0]); + + if (heldCategory == ToolCategory.None || heldCategory != neededCategory) + return false; + + // Check tool tier requirement + int heldTier = GetToolTier(toolType); + int requiredTier = GetRequiredTier(blockMaterial, recommended); + + return heldTier >= requiredTier; + } + + /// + /// Get the minimum tool tier required for a block based on Material2Tool's recommendation ordering. + /// + private static int GetRequiredTier(Material blockMaterial, ItemType[] recommended) + { + if (recommended.Length == 0) + return 0; + + // Material2Tool lists tools from highest to lowest tier. + // The last tool in the array is the minimum required tier. + return GetToolTier(recommended[^1]); + } + + private enum ToolCategory + { + None, + Pickaxe, + Axe, + Shovel, + Hoe, + Sword, + Shears + } + + private static ToolCategory GetToolCategory(ItemType item) + { + if (IsPickaxe(item)) return ToolCategory.Pickaxe; + if (IsAxe(item)) return ToolCategory.Axe; + if (IsShovel(item)) return ToolCategory.Shovel; + if (IsHoe(item)) return ToolCategory.Hoe; + if (IsSword(item)) return ToolCategory.Sword; + if (item == ItemType.Shears) return ToolCategory.Shears; + return ToolCategory.None; + } + + private static int GetToolTier(ItemType item) + { + string name = item.ToString(); + if (name.StartsWith("Wooden")) return 0; + if (name.StartsWith("Golden")) return 0; + if (name.StartsWith("Stone")) return 1; + if (name.StartsWith("Iron")) return 2; + if (name.StartsWith("Diamond")) return 3; + if (name.StartsWith("Netherite")) return 4; + return 0; + } + + private static bool IsPickaxe(ItemType item) => + item is ItemType.WoodenPickaxe or ItemType.StonePickaxe or ItemType.IronPickaxe + or ItemType.GoldenPickaxe or ItemType.DiamondPickaxe or ItemType.NetheritePickaxe; + + private static bool IsAxe(ItemType item) => + item is ItemType.WoodenAxe or ItemType.StoneAxe or ItemType.IronAxe + or ItemType.GoldenAxe or ItemType.DiamondAxe or ItemType.NetheriteAxe; + + private static bool IsShovel(ItemType item) => + item is ItemType.WoodenShovel or ItemType.StoneShovel or ItemType.IronShovel + or ItemType.GoldenShovel or ItemType.DiamondShovel or ItemType.NetheriteShovel; + + private static bool IsHoe(ItemType item) => + item is ItemType.WoodenHoe or ItemType.StoneHoe or ItemType.IronHoe + or ItemType.GoldenHoe or ItemType.DiamondHoe or ItemType.NetheriteHoe; + + private static bool IsSword(ItemType item) => + item is ItemType.WoodenSword or ItemType.StoneSword or ItemType.IronSword + or ItemType.GoldenSword or ItemType.DiamondSword or ItemType.NetheriteSword; + + private static bool IsShearable(Material block) => + block is Material.Cobweb or Material.OakLeaves or Material.SpruceLeaves + or Material.BirchLeaves or Material.JungleLeaves or Material.AcaciaLeaves + or Material.DarkOakLeaves or Material.CherryLeaves or Material.MangroveLeaves + or Material.AzaleaLeaves or Material.FloweringAzaleaLeaves + or Material.WhiteWool or Material.OrangeWool or Material.MagentaWool + or Material.LightBlueWool or Material.YellowWool or Material.LimeWool + or Material.PinkWool or Material.GrayWool or Material.LightGrayWool + or Material.CyanWool or Material.PurpleWool or Material.BlueWool + or Material.BrownWool or Material.GreenWool or Material.RedWool + or Material.BlackWool or Material.Vine; + + #endregion + } +} diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index 24069342..edfa9cec 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -107,6 +107,9 @@ namespace MinecraftClient // player effects private readonly Dictionary playerEffects = new(); + + // player attributes (e.g., block_break_speed, mining_efficiency, submerged_mining_speed) + private readonly Dictionary playerAttributes = new(); // Sneaking public bool IsSneaking { get; set; } = false; @@ -2591,6 +2594,13 @@ namespace MinecraftClient if (lookAtBlock) UpdateLocation(GetCurrentLocation(), location); + // Auto-compute dig duration for survival/adventure mode when not explicitly supplied + if (duration <= 0 && protocolversion >= Protocol18Handler.MC_1_8_Version + && gamemode is 0 or 2) // Survival or Adventure + { + duration = ComputeAutoDigDuration(location); + } + // Send dig start and dig end, will need to wait for server response to know dig result // See https://wiki.vg/How_to_Write_a_Client#Digging for more details bool result = handler.SendPlayerDigging(0, location, blockFace, sequenceId++) @@ -2608,6 +2618,52 @@ namespace MinecraftClient } } + /// + /// Compute the automatic dig duration in seconds for a block, based on held tool, + /// enchantments, effects, attributes, and player state. + /// Returns 0 for instant-break blocks. + /// + private double ComputeAutoDigDuration(Location location) + { + try + { + Block block = world.GetBlock(location); + Material blockMaterial = block.Type; + + if (blockMaterial == Material.Air) + return 0; + + // Get held item from player inventory + Item? heldItem = null; + Item? helmetItem = null; + if (inventories.TryGetValue(0, out var playerInv)) + { + int hotbarSlot = 36 + CurrentSlot; // Hotbar slots are 36-44 + playerInv.Items.TryGetValue(hotbarSlot, out heldItem); + playerInv.Items.TryGetValue(5, out helmetItem); // Slot 5 = helmet + } + + int ticks = MiningCalculator.ComputeDigTicks( + blockMaterial, + heldItem, + helmetItem, + playerEffects, + playerAttributes, + playerPhysics.InWater, + playerPhysics.OnGround, + protocolversion); + + if (ticks <= 0) + return 0; + + return (double)ticks / Settings.ClientTicksPerSecond; + } + catch + { + return 0; + } + } + /// /// Change active slot in the player inventory /// @@ -3712,6 +3768,9 @@ namespace MinecraftClient { if (EntityID == playerEntityID) { + foreach (var kvp in prop) + playerAttributes[kvp.Key] = kvp.Value; + DispatchBotEvent(bot => bot.OnPlayerProperty(prop)); } } diff --git a/MinecraftClient/Scripting/ChatBot.cs b/MinecraftClient/Scripting/ChatBot.cs index f62e1377..912b1c49 100644 --- a/MinecraftClient/Scripting/ChatBot.cs +++ b/MinecraftClient/Scripting/ChatBot.cs @@ -1089,9 +1089,10 @@ namespace MinecraftClient.Scripting /// Example: if your player is under a block that is being destroyed, use Down /// Also perform the "arm swing" animation /// Also look at the block before digging - protected bool DigBlock(Location location, Direction direction, bool swingArms = true, bool lookAtBlock = true) + /// Dig duration in seconds. 0 = auto-compute for survival, or instant for creative + protected bool DigBlock(Location location, Direction direction, bool swingArms = true, bool lookAtBlock = true, double duration = 0) { - return Handler.DigBlock(location, direction, swingArms, lookAtBlock); + return Handler.DigBlock(location, direction, swingArms, lookAtBlock, duration); } ///