diff --git a/.gitignore b/.gitignore
index d0f86370..7d0e131b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -436,5 +436,8 @@ FodyWeavers.xsd
# SpecStory files
/.specstory/
/.vscode/settings.json
+
+# Other
/Sentry/
+/downloads/
server.pid
diff --git a/MinecraftClient/Mapping/BlockHardness.cs b/MinecraftClient/Mapping/BlockHardness.cs
new file mode 100644
index 00000000..84cd8f92
--- /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.2f },
+ { Material.OakLog, 2.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..6f49fd4a
--- /dev/null
+++ b/MinecraftClient/Mapping/MiningCalculator.cs
@@ -0,0 +1,572 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using MinecraftClient.Inventory;
+using MinecraftClient.Protocol.Handlers;
+using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_20_6;
+using MinecraftClient.Protocol.Handlers.StructuredComponents.Components._1_21_5;
+using MinecraftClient.Protocol.Handlers.StructuredComponents.Components.Subcomponents._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 (speed > 1.0f)
+ {
+ speed += GetEfficiencyBonus(heldItem, playerAttributes, protocolVersion);
+ }
+
+ int digSpeedAmplifier = GetDigSpeedAmplifier(effects);
+ if (digSpeedAmplifier >= 0)
+ speed *= 1.0f + (digSpeedAmplifier + 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
+ && TryGetToolRules(heldItem, out List? rules, out float defaultMiningSpeed))
+ {
+ foreach (var rule in rules)
+ {
+ if (rule.HasSpeed && MatchesBlockSet(rule.Blocks, blockMaterial))
+ return rule.Speed;
+ }
+
+ // Structured tool data covers modern mining rules, but keep the legacy fallback for
+ // explicit block holder-sets that MCC cannot resolve yet (for example cobweb).
+ if (defaultMiningSpeed > 1.0f)
+ return 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
+ && TryGetToolRules(heldItem, out List? rules, out _))
+ {
+ foreach (var rule in rules)
+ {
+ if (rule.HasCorrectDropForBlocks && MatchesBlockSet(rule.Blocks, blockMaterial))
+ return rule.CorrectDropForBlocks;
+ }
+ }
+
+ // Legacy path, plus a modern fallback for direct block holder-sets MCC cannot resolve yet.
+ return IsCorrectToolLegacy(heldItem.Type, blockMaterial);
+ }
+
+ private static bool TryGetToolRules(
+ Item heldItem,
+ [NotNullWhen(true)] out List? rules,
+ out float defaultMiningSpeed)
+ {
+ rules = null;
+ defaultMiningSpeed = 1.0f;
+
+ if (heldItem.Components is null)
+ return false;
+
+ if (heldItem.Components.OfType().FirstOrDefault() is ToolComponent toolComponent)
+ {
+ rules = toolComponent.Rules;
+ defaultMiningSpeed = toolComponent.DefaultMiningSpeed;
+ return true;
+ }
+
+ if (heldItem.Components.OfType().FirstOrDefault() is ToolComponent1215 toolComponent1215)
+ {
+ rules = toolComponent1215.Rules;
+ defaultMiningSpeed = toolComponent1215.DefaultMiningSpeed;
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// 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);
+ return tag switch
+ {
+ "mineable/pickaxe" => tools.Length > 0 && IsPickaxe(tools[0]),
+ "mineable/axe" => tools.Length > 0 && IsAxe(tools[0]),
+ "mineable/shovel" => tools.Length > 0 && IsShovel(tools[0]),
+ "mineable/hoe" => tools.Length > 0 && IsHoe(tools[0]),
+ "leaves" => IsLeaf(blockMaterial),
+ "wool" => IsWool(blockMaterial),
+ "incorrect_for_wooden_tool" => RequiresHigherTier(blockMaterial, 0),
+ "incorrect_for_gold_tool" => RequiresHigherTier(blockMaterial, 0),
+ "incorrect_for_stone_tool" => RequiresHigherTier(blockMaterial, 1),
+ "incorrect_for_copper_tool" => RequiresHigherTier(blockMaterial, 1),
+ "incorrect_for_iron_tool" => RequiresHigherTier(blockMaterial, 2),
+ "incorrect_for_diamond_tool" => RequiresHigherTier(blockMaterial, 3),
+ "incorrect_for_netherite_tool" => RequiresHigherTier(blockMaterial, 4),
+ _ => false
+ };
+ }
+
+ private static bool RequiresHigherTier(Material blockMaterial, int tier)
+ {
+ ItemType[] recommended = Material2Tool.GetCorrectToolForBlock(blockMaterial);
+ if (recommended.Length == 0)
+ return false;
+
+ return GetRequiredTier(blockMaterial, recommended) > tier;
+ }
+
+ ///
+ /// 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)
+ {
+ float specialToolSpeed = toolType switch
+ {
+ ItemType.Shears => GetShearsSpeed(blockMaterial),
+ _ when IsSword(toolType) && blockMaterial == Material.Cobweb => 15.0f,
+ _ => 1.0f
+ };
+
+ if (specialToolSpeed > 1.0f)
+ return specialToolSpeed;
+
+ 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)
+ 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)
+ {
+ if (toolType == ItemType.Shears && blockMaterial == Material.Cobweb)
+ return true;
+ if (IsSword(toolType) && blockMaterial == Material.Cobweb)
+ return true;
+ 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 float GetShearsSpeed(Material block)
+ {
+ return block switch
+ {
+ Material.Cobweb => 15.0f,
+ Material.Vine or Material.GlowLichen => 2.0f,
+ _ when IsLeaf(block) => 15.0f,
+ _ when IsWool(block) => 5.0f,
+ _ => 1.0f
+ };
+ }
+
+ private static bool IsShearable(Material block) =>
+ block == Material.Cobweb || IsLeaf(block) || IsWool(block) || block is Material.Vine or Material.GlowLichen;
+
+ private static bool IsLeaf(Material block) =>
+ block is 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.PaleOakLeaves;
+
+ private static bool IsWool(Material block) =>
+ block is 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;
+
+ private static float GetEfficiencyBonus(Item? heldItem, Dictionary playerAttributes, int protocolVersion)
+ {
+ if (protocolVersion >= Protocol18Handler.MC_1_21_11_Version
+ && playerAttributes.TryGetValue("player.mining_efficiency", out double miningEfficiency)
+ && miningEfficiency > 0.0)
+ {
+ return (float)miningEfficiency;
+ }
+
+ int efficiencyLevel = GetEnchantmentLevel(heldItem, Enchantments.Efficiency, protocolVersion);
+ return efficiencyLevel > 0 ? efficiencyLevel * efficiencyLevel + 1 : 0.0f;
+ }
+
+ private static int GetDigSpeedAmplifier(Dictionary effects)
+ {
+ int amplifier = -1;
+
+ if (effects.TryGetValue(Effects.Haste, out var hasteData))
+ amplifier = Math.Max(amplifier, hasteData.Amplifier);
+
+ if (effects.TryGetValue(Effects.ConduitPower, out var conduitData))
+ amplifier = Math.Max(amplifier, conduitData.Amplifier);
+
+ return amplifier;
+ }
+
+ #endregion
+ }
+}
diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs
index 3eabfbd8..bd84d562 100644
--- a/MinecraftClient/McClient.cs
+++ b/MinecraftClient/McClient.cs
@@ -110,6 +110,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;
@@ -2630,6 +2633,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++)
@@ -2647,6 +2657,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
///
@@ -3751,6 +3807,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/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs
index 45bde28f..88c26690 100644
--- a/MinecraftClient/Protocol/Handlers/Protocol18.cs
+++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs
@@ -2514,7 +2514,7 @@ namespace MinecraftClient.Protocol.Handlers
if (handler.GetEntityHandlingEnabled())
{
var entityId = dataTypes.ReadNextVarInt(packetData);
- var effectId = protocolVersion >= MC_1_18_2_Version
+ var effectId = protocolVersion >= MC_1_20_4_Version
? dataTypes.ReadNextVarInt(packetData) + 1
: dataTypes.ReadNextByte(packetData);
@@ -2544,7 +2544,7 @@ namespace MinecraftClient.Protocol.Handlers
if (handler.GetEntityHandlingEnabled())
{
var entityId = dataTypes.ReadNextVarInt(packetData);
- var effectId = protocolVersion >= MC_1_18_2_Version
+ var effectId = protocolVersion >= MC_1_20_4_Version
? dataTypes.ReadNextVarInt(packetData) + 1
: dataTypes.ReadNextByte(packetData);
diff --git a/MinecraftClient/Scripting/ChatBot.cs b/MinecraftClient/Scripting/ChatBot.cs
index 422fc1a4..2776cda9 100644
--- a/MinecraftClient/Scripting/ChatBot.cs
+++ b/MinecraftClient/Scripting/ChatBot.cs
@@ -1097,9 +1097,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);
}
///