From c95fe131e31ad63142619bab0122352131d6c46d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 30 Mar 2026 07:40:08 +0000
Subject: [PATCH 1/4] Initial plan
From 22455905a7952a706024f852ac21c25ca01ca1b5 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 30 Mar 2026 08:24:00 +0000
Subject: [PATCH 2/4] Add automatic mining-speed and dig-duration handling
- Add BlockHardness.cs with hardness data for all 1053 blocks from MC 1.21.11
- Add MiningCalculator.cs with version-aware dig duration computation
- Tool speed from ToolComponent (1.20.6+) or legacy hardcoded tables
- Efficiency enchantment (legacy: level^2+1, 1.21.11+: mining_efficiency attribute)
- Haste/Conduit Power/Mining Fatigue effects
- BLOCK_BREAK_SPEED attribute (1.20.6+)
- MINING_EFFICIENCY and SUBMERGED_MINING_SPEED attributes (1.21.11+)
- Underwater penalty with Aqua Affinity support (legacy) or attribute (modern)
- Airborne penalty
- Correct tool for drops check (30 vs 100 divisor)
- Modify McClient.DigBlock to auto-compute duration for survival/adventure mode
- Cache player attributes from OnEntityProperties in McClient
- Expose duration parameter in ChatBot.cs scripting wrapper
- Add /downloads/ to .gitignore
Agent-Logs-Url: https://github.com/MCCTeam/Minecraft-Console-Client/sessions/579df446-a335-4174-9a8b-4d66173c82b1
Co-authored-by: milutinke <441903+milutinke@users.noreply.github.com>
---
.gitignore | 1 +
MinecraftClient/Mapping/BlockHardness.cs | 1326 +++++++++++++++++++
MinecraftClient/Mapping/MiningCalculator.cs | 493 +++++++
MinecraftClient/McClient.cs | 59 +
MinecraftClient/Scripting/ChatBot.cs | 5 +-
5 files changed, 1882 insertions(+), 2 deletions(-)
create mode 100644 MinecraftClient/Mapping/BlockHardness.cs
create mode 100644 MinecraftClient/Mapping/MiningCalculator.cs
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);
}
///
From d158bfdf21a7b8b9c6df90e159c09db0c3166395 Mon Sep 17 00:00:00 2001
From: Anon
Date: Mon, 30 Mar 2026 20:20:58 +0200
Subject: [PATCH 3/4] Fixed bugs
---
MinecraftClient/Mapping/BlockHardness.cs | 4 +-
MinecraftClient/Mapping/MiningCalculator.cs | 199 ++++++++++++++------
2 files changed, 141 insertions(+), 62 deletions(-)
diff --git a/MinecraftClient/Mapping/BlockHardness.cs b/MinecraftClient/Mapping/BlockHardness.cs
index 311507b0..84cd8f92 100644
--- a/MinecraftClient/Mapping/BlockHardness.cs
+++ b/MinecraftClient/Mapping/BlockHardness.cs
@@ -249,8 +249,8 @@ namespace MinecraftClient.Mapping
{ Material.NetherSprouts, 0.0f },
{ Material.NetherWart, 0.0f },
{ Material.OakButton, 0.0f },
- { Material.OakLeaves, 0.0f },
- { Material.OakLog, 0.0f },
+ { Material.OakLeaves, 0.2f },
+ { Material.OakLog, 2.0f },
{ Material.OakSapling, 0.0f },
{ Material.OpenEyeblossom, 0.0f },
{ Material.OrangeCandle, 0.0f },
diff --git a/MinecraftClient/Mapping/MiningCalculator.cs b/MinecraftClient/Mapping/MiningCalculator.cs
index 38cf6d18..6f49fd4a 100644
--- a/MinecraftClient/Mapping/MiningCalculator.cs
+++ b/MinecraftClient/Mapping/MiningCalculator.cs
@@ -1,9 +1,12 @@
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
{
@@ -74,27 +77,14 @@ namespace MinecraftClient.Mapping
{
float speed = GetToolSpeed(blockMaterial, heldItem, protocolVersion);
- if (protocolVersion >= Protocol18Handler.MC_1_21_11_Version)
+ if (speed > 1.0f)
{
- // 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;
+ speed += GetEfficiencyBonus(heldItem, playerAttributes, protocolVersion);
}
- // 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;
+ int digSpeedAmplifier = GetDigSpeedAmplifier(effects);
+ if (digSpeedAmplifier >= 0)
+ speed *= 1.0f + (digSpeedAmplifier + 1) * 0.2f;
// Mining Fatigue
if (effects.TryGetValue(Effects.MiningFatigue, out var fatigueData))
@@ -155,19 +145,19 @@ namespace MinecraftClient.Mapping
return 1.0f;
// Modern path: use ToolComponent from structured components
- if (protocolVersion >= Protocol18Handler.MC_1_20_6_Version)
+ if (protocolVersion >= Protocol18Handler.MC_1_20_6_Version
+ && TryGetToolRules(heldItem, out List? rules, out float defaultMiningSpeed))
{
- var toolComp = heldItem.Components?.OfType().FirstOrDefault();
- if (toolComp is not null)
+ foreach (var rule in rules)
{
- // Check rules for matching blocks
- foreach (var rule in toolComp.Rules)
- {
- if (rule.HasSpeed && MatchesBlockSet(rule.Blocks, blockMaterial))
- return rule.Speed;
- }
- return toolComp.DefaultMiningSpeed;
+ 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
@@ -186,25 +176,48 @@ namespace MinecraftClient.Mapping
return false;
// Modern path: check ToolComponent rules
- if (protocolVersion >= Protocol18Handler.MC_1_20_6_Version)
+ if (protocolVersion >= Protocol18Handler.MC_1_20_6_Version
+ && TryGetToolRules(heldItem, out List? rules, out _))
{
- var toolComp = heldItem.Components?.OfType().FirstOrDefault();
- if (toolComp is not null)
+ foreach (var rule in rules)
{
- foreach (var rule in toolComp.Rules)
- {
- if (rule.HasCorrectDropForBlocks && rule.CorrectDropForBlocks
- && MatchesBlockSet(rule.Blocks, blockMaterial))
- return true;
- }
+ if (rule.HasCorrectDropForBlocks && MatchesBlockSet(rule.Blocks, blockMaterial))
+ return rule.CorrectDropForBlocks;
}
- return false;
}
- // Legacy path: check if Material2Tool recommends this tool type
+ // 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.
///
@@ -241,20 +254,34 @@ namespace MinecraftClient.Mapping
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),
+ "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.
///
@@ -330,6 +357,16 @@ namespace MinecraftClient.Mapping
///
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;
@@ -339,14 +376,7 @@ namespace MinecraftClient.Mapping
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);
}
@@ -400,7 +430,13 @@ namespace MinecraftClient.Mapping
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);
@@ -476,17 +512,60 @@ namespace MinecraftClient.Mapping
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 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
+ 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 or Material.Vine;
+ 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
}
From 5de72de234a32262398e75c19e4e4c7b3888b0c9 Mon Sep 17 00:00:00 2001
From: Anon
Date: Mon, 30 Mar 2026 21:37:53 +0200
Subject: [PATCH 4/4] Fixed a wrong effect decoding on 1.20.4
---
MinecraftClient/Protocol/Handlers/Protocol18.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs
index eeabc8e0..8fd192a7 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);