diff --git a/connector/pom.xml b/connector/pom.xml
index 0258e9a30..a14fcfc4d 100644
--- a/connector/pom.xml
+++ b/connector/pom.xml
@@ -48,6 +48,24 @@
             <version>8.1.1</version>
             <scope>compile</scope>
         </dependency>
+        <dependency>
+            <groupId>com.nukkitx.fastutil</groupId>
+            <artifactId>fastutil-int-double-maps</artifactId>
+            <version>8.3.1</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.nukkitx.fastutil</groupId>
+            <artifactId>fastutil-int-boolean-maps</artifactId>
+            <version>8.3.1</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.nukkitx.fastutil</groupId>
+            <artifactId>fastutil-object-int-maps</artifactId>
+            <version>8.3.1</version>
+            <scope>compile</scope>
+        </dependency>
         <dependency>
             <groupId>com.nukkitx.fastutil</groupId>
             <artifactId>fastutil-object-byte-maps</artifactId>
diff --git a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java
index 56d59ec3d..c7a0f96ca 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java
@@ -39,6 +39,7 @@ import lombok.Setter;
 import org.geysermc.connector.GeyserConnector;
 import org.geysermc.connector.entity.type.EntityType;
 import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.session.cache.EntityEffectCache;
 import org.geysermc.connector.utils.SkinUtils;
 
 import java.util.UUID;
@@ -50,6 +51,7 @@ public class PlayerEntity extends LivingEntity {
     private String username;
     private long lastSkinUpdate = -1;
     private boolean playerList = true;
+    private final EntityEffectCache effectCache;
 
     public PlayerEntity(GameProfile gameProfile, long entityId, long geyserId, Vector3f position, Vector3f motion, Vector3f rotation) {
         super(entityId, geyserId, EntityType.PLAYER, position, motion, rotation);
@@ -57,6 +59,7 @@ public class PlayerEntity extends LivingEntity {
         profile = gameProfile;
         uuid = gameProfile.getId();
         username = gameProfile.getName();
+        effectCache = new EntityEffectCache();
         if (geyserId == 1) valid = true;
     }
 
diff --git a/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java b/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java
index a11ce856b..52fb786bc 100644
--- a/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java
+++ b/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java
@@ -50,6 +50,6 @@ public class PlayerInventory extends Inventory {
     }
 
     public ItemStack getItemInHand() {
-        return items[heldItemSlot];
+        return items[36 + heldItemSlot];
     }
 }
diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityEffectCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityEffectCache.java
new file mode 100644
index 000000000..a16ef6902
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityEffectCache.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.session.cache;
+
+import com.github.steveice10.mc.protocol.data.game.entity.Effect;
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
+import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
+import lombok.Getter;
+
+public class EntityEffectCache {
+
+    @Getter
+    private final Object2IntMap<Effect> entityEffects = new Object2IntOpenHashMap<>();
+
+    public void addEffect(Effect effect, int effectAmplifier) {
+        if (effect != null) {
+            entityEffects.putIfAbsent(effect, effectAmplifier + 1);
+        }
+    }
+
+    public void removeEffect(Effect effect) {
+        if (entityEffects.containsKey(effect)) {
+            int effectLevel = entityEffects.getInt(effect);
+            entityEffects.remove(effect, effectLevel);
+        }
+    }
+
+    public int getEffectLevel(Effect effect) {
+        return entityEffects.getOrDefault(effect, 0);
+    }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/Registry.java b/connector/src/main/java/org/geysermc/connector/network/translators/Registry.java
index f32fe4b6d..866a454f3 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/Registry.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/Registry.java
@@ -47,12 +47,15 @@ public class Registry<T> {
         BEDROCK.MAP.put(clazz, translator);
     }
 
+    @SuppressWarnings("unchecked")
     public <P extends T> boolean translate(Class<? extends P> clazz, P packet, GeyserSession session) {
         if (!session.getUpstream().isClosed() && !session.isClosed()) {
             try {
                 if (MAP.containsKey(clazz)) {
                     ((PacketTranslator<P>) MAP.get(clazz)).translate(packet, session);
                     return true;
+                } else {
+                    GeyserConnector.getInstance().getLogger().debug("Could not find packet for " + (packet.toString().length() > 25 ? packet.getClass().getSimpleName() : packet));
                 }
             } catch (Throwable ex) {
                 GeyserConnector.getInstance().getLogger().error("Could not translate packet " + packet.getClass().getSimpleName(), ex);
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java
index 8ddfd8334..2bef0b7d2 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockTranslator.java
@@ -54,6 +54,13 @@ public class BlockTranslator {
     private static final Object2ByteOpenHashMap<BlockState> BED_COLORS = new Object2ByteOpenHashMap<>();
 
     private static final Map<BlockState, String> JAVA_ID_TO_BLOCK_ENTITY_MAP = new HashMap<>();
+    public static final Int2DoubleMap JAVA_RUNTIME_ID_TO_HARDNESS = new Int2DoubleOpenHashMap();
+    public static final Int2BooleanMap JAVA_RUNTIME_ID_TO_CAN_HARVEST_WITH_HAND = new Int2BooleanOpenHashMap();
+    public static final Int2ObjectMap<String> JAVA_RUNTIME_ID_TO_TOOL_TYPE = new Int2ObjectOpenHashMap<>();
+
+    // For block breaking animation math
+    public static final List<Integer> JAVA_RUNTIME_WOOL_IDS = new ArrayList<>();
+    public static final int JAVA_RUNTIME_COBWEB_ID;
 
     private static final int BLOCK_STATE_VERSION = 17760256;
 
@@ -89,6 +96,7 @@ public class BlockTranslator {
         int waterRuntimeId = -1;
         int javaRuntimeId = -1;
         int bedrockRuntimeId = 0;
+        int cobwebRuntimeId = -1;
         Iterator<Map.Entry<String, JsonNode>> blocksIterator = blocks.fields();
         while (blocksIterator.hasNext()) {
             javaRuntimeId++;
@@ -97,6 +105,27 @@ public class BlockTranslator {
             BlockState javaBlockState = new BlockState(javaRuntimeId);
             CompoundTag blockTag = buildBedrockState(entry.getValue());
 
+            // TODO fix this, (no block should have a null hardness)
+            JsonNode hardnessNode = entry.getValue().get("block_hardness");
+            if (hardnessNode != null) {
+                JAVA_RUNTIME_ID_TO_HARDNESS.put(javaRuntimeId, hardnessNode.doubleValue());
+            }
+
+            JAVA_RUNTIME_ID_TO_CAN_HARVEST_WITH_HAND.put(javaRuntimeId, entry.getValue().get("can_break_with_hand").booleanValue());
+
+            JsonNode toolTypeNode = entry.getValue().get("tool_type");
+            if (toolTypeNode != null) {
+                JAVA_RUNTIME_ID_TO_TOOL_TYPE.put(javaRuntimeId, toolTypeNode.textValue());
+            }
+
+            if (javaId.contains("wool")) {
+                JAVA_RUNTIME_WOOL_IDS.add(javaRuntimeId);
+            }
+
+            if (javaId.contains("cobweb")) {
+                cobwebRuntimeId = javaRuntimeId;
+            }
+
             JAVA_ID_BLOCK_MAP.put(javaId, javaBlockState);
 
             if (javaId.contains("sign[")) {
@@ -142,6 +171,11 @@ public class BlockTranslator {
             bedrockRuntimeId++;
         }
 
+        if (cobwebRuntimeId == -1) {
+            throw new AssertionError("Unable to find cobwebs in palette");
+        }
+        JAVA_RUNTIME_COBWEB_ID = cobwebRuntimeId;
+
         if (waterRuntimeId == -1) {
             throw new AssertionError("Unable to find water in palette");
         }
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java
index fd4f0b020..e579c20ee 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemEntry.java
@@ -34,11 +34,11 @@ public class ItemEntry {
 
     public static ItemEntry AIR = new ItemEntry("minecraft:air", 0, 0, 0);
 
-    private String javaIdentifier;
-    private int javaId;
+    private final String javaIdentifier;
+    private final int javaId;
 
-    private int bedrockId;
-    private int bedrockData;
+    private final int bedrockId;
+    private final int bedrockData;
 
     @Override
     public boolean equals(Object obj) {
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ToolItemEntry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ToolItemEntry.java
new file mode 100644
index 000000000..5d1ddd262
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ToolItemEntry.java
@@ -0,0 +1,15 @@
+package org.geysermc.connector.network.translators.item;
+
+import lombok.Getter;
+
+@Getter
+public class ToolItemEntry extends ItemEntry {
+    private final String toolType;
+    private final String toolTier;
+
+    public ToolItemEntry(String javaIdentifier, int javaId, int bedrockId, int bedrockData, String toolType, String toolTier) {
+        super(javaIdentifier, javaId, bedrockId, bedrockData);
+        this.toolType = toolType;
+        this.toolTier = toolTier;
+    }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityEffectTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityEffectTranslator.java
index 9716d9597..590afbd07 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityEffectTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityEffectTranslator.java
@@ -28,6 +28,7 @@ package org.geysermc.connector.network.translators.java.entity;
 import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityEffectPacket;
 import com.nukkitx.protocol.bedrock.packet.MobEffectPacket;
 import org.geysermc.connector.entity.Entity;
+import org.geysermc.connector.entity.PlayerEntity;
 import org.geysermc.connector.network.session.GeyserSession;
 import org.geysermc.connector.network.translators.PacketTranslator;
 import org.geysermc.connector.utils.EntityUtils;
@@ -39,6 +40,7 @@ public class JavaEntityEffectTranslator extends PacketTranslator<ServerEntityEff
         Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
         if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
             entity = session.getPlayerEntity();
+            ((PlayerEntity) entity).getEffectCache().addEffect(packet.getEffect(), packet.getAmplifier());
         }
         if (entity == null)
             return;
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityRemoveEffectTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityRemoveEffectTranslator.java
index 97837bb81..db8a5329d 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityRemoveEffectTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityRemoveEffectTranslator.java
@@ -28,6 +28,7 @@ package org.geysermc.connector.network.translators.java.entity;
 import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityRemoveEffectPacket;
 import com.nukkitx.protocol.bedrock.packet.MobEffectPacket;
 import org.geysermc.connector.entity.Entity;
+import org.geysermc.connector.entity.PlayerEntity;
 import org.geysermc.connector.network.session.GeyserSession;
 import org.geysermc.connector.network.translators.PacketTranslator;
 import org.geysermc.connector.utils.EntityUtils;
@@ -39,6 +40,7 @@ public class JavaEntityRemoveEffectTranslator extends PacketTranslator<ServerEnt
         Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
         if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
             entity = session.getPlayerEntity();
+            ((PlayerEntity) entity).getEffectCache().removeEffect(packet.getEffect());
         }
         if (entity == null)
             return;
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerActionAckTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerActionAckTranslator.java
index 451081a1a..ba39e7068 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerActionAckTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerActionAckTranslator.java
@@ -25,19 +25,64 @@
 
 package org.geysermc.connector.network.translators.java.entity.player;
 
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
 import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerActionAckPacket;
+import com.github.steveice10.opennbt.tag.builtin.*;
+import com.nukkitx.math.vector.Vector3f;
+import com.nukkitx.protocol.bedrock.data.LevelEventType;
+import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
+import org.geysermc.connector.inventory.PlayerInventory;
 import org.geysermc.connector.network.session.GeyserSession;
 import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.network.translators.TranslatorsInit;
+import org.geysermc.connector.network.translators.block.BlockTranslator;
+import org.geysermc.connector.network.translators.item.ItemEntry;
+import org.geysermc.connector.utils.BlockUtils;
 import org.geysermc.connector.utils.ChunkUtils;
 
 public class JavaPlayerActionAckTranslator extends PacketTranslator<ServerPlayerActionAckPacket> {
 
     @Override
     public void translate(ServerPlayerActionAckPacket packet, GeyserSession session) {
+        LevelEventPacket levelEvent = new LevelEventPacket();
         switch (packet.getAction()) {
             case FINISH_DIGGING:
                 ChunkUtils.updateBlock(session, packet.getNewState(), packet.getPosition());
                 break;
+
+            case START_DIGGING:
+                levelEvent.setType(LevelEventType.BLOCK_START_BREAK);
+                levelEvent.setPosition(Vector3f.from(
+                        packet.getPosition().getX(),
+                        packet.getPosition().getY(),
+                        packet.getPosition().getZ()
+                ));
+                double blockHardness = BlockTranslator.JAVA_RUNTIME_ID_TO_HARDNESS.get(packet.getNewState().getId());
+                PlayerInventory inventory = session.getInventory();
+                ItemStack item = inventory.getItemInHand();
+                ItemEntry itemEntry = null;
+                CompoundTag nbtData = new CompoundTag("");
+                if (item != null) {
+                    itemEntry = TranslatorsInit.getItemTranslator().getItem(item);
+                    nbtData = item.getNbt();
+                }
+                double breakTime = Math.ceil(BlockUtils.getBreakTime(blockHardness, packet.getNewState().getId(), itemEntry, nbtData, session.getPlayerEntity()) * 20);
+                levelEvent.setData((int) (65535 / breakTime));
+                session.getUpstream().sendPacket(levelEvent);
+                break;
+
+            case CANCEL_DIGGING:
+                levelEvent.setType(LevelEventType.BLOCK_STOP_BREAK);
+                levelEvent.setPosition(Vector3f.from(
+                        packet.getPosition().getX(),
+                        packet.getPosition().getY(),
+                        packet.getPosition().getZ()
+                ));
+                levelEvent.setData(0);
+                session.getUpstream().sendPacket(levelEvent);
+                break;
         }
     }
 }
+
+
diff --git a/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java b/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java
new file mode 100644
index 000000000..34287073e
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.utils;
+
+import com.github.steveice10.mc.protocol.data.game.entity.Effect;
+import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
+import org.geysermc.connector.entity.PlayerEntity;
+import org.geysermc.connector.network.translators.block.BlockTranslator;
+import org.geysermc.connector.network.translators.item.ItemEntry;
+import org.geysermc.connector.network.translators.item.ToolItemEntry;
+
+public class BlockUtils {
+
+    private static boolean correctTool(String blockToolType, String itemToolType) {
+        return (blockToolType.equals("sword") && itemToolType.equals("sword")) ||
+                (blockToolType.equals("shovel") && itemToolType.equals("shovel")) ||
+                (blockToolType.equals("pickaxe") && itemToolType.equals("pickaxe")) ||
+                (blockToolType.equals("axe") && itemToolType.equals("axe")) ||
+                (blockToolType.equals("shears") && itemToolType.equals("shears"));
+    }
+
+    private static double toolBreakTimeBonus(String toolType, String toolTier, boolean isWoolBlock) {
+        if (toolType.equals("shears")) return isWoolBlock ? 5.0 : 15.0;
+        if (toolType.equals("")) return 1.0;
+        switch (toolTier) {
+            case "wooden":
+                return 2.0;
+            case "stone":
+                return 4.0;
+            case "iron":
+                return 6.0;
+            case "diamond":
+                return 8.0;
+            case "golden":
+                return 12.0;
+            default:
+                return 1.0;
+        }
+    }
+
+    //http://minecraft.gamepedia.com/Breaking
+    private static double calculateBreakTime(double blockHardness, String toolTier, boolean canHarvestWithHand, boolean correctTool,
+                                             String toolType, boolean isWoolBlock, boolean isCobweb, int toolEfficiencyLevel, int hasteLevel, int miningFatigueLevel
+            /*boolean insideOfWaterWithoutAquaAffinity, boolean outOfWaterButNotOnGround*/) {
+        double baseTime = ((correctTool || canHarvestWithHand) ? 1.5 : 5.0) * blockHardness;
+        double speed = 1.0 / baseTime;
+
+        if (correctTool) {
+            speed *= toolBreakTimeBonus(toolType, toolTier, isWoolBlock);
+            speed += toolEfficiencyLevel == 0 ? 0 : toolEfficiencyLevel * toolEfficiencyLevel + 1;
+        } else if (toolType.equals("sword")) {
+            speed*= (isCobweb ? 15.0 : 1.5);
+        }
+        speed *= 1.0 + (0.2 * hasteLevel);
+
+        switch (miningFatigueLevel) {
+            case 0:
+                break;
+            case 1:
+                speed -= (speed * 0.7);
+                break;
+            case 2:
+                speed -= (speed * 0.91);
+                break;
+            case 3:
+                speed -= (speed * 0.9973);
+                break;
+            default:
+                speed -= (speed * 0.99919);
+                break;
+        }
+
+        //if (insideOfWaterWithoutAquaAffinity) speed *= 0.2;
+        //if (outOfWaterButNotOnGround) speed *= 0.2;
+        // else if insideWaterAndNotOnGround speed *= 0.2;
+        return 1.0 / speed;
+    }
+
+    public static double getBreakTime(double blockHardness, int blockId, ItemEntry item, CompoundTag nbtData, PlayerEntity player) {
+        boolean isWoolBlock = BlockTranslator.JAVA_RUNTIME_WOOL_IDS.contains(blockId);
+        boolean isCobweb = blockId == BlockTranslator.JAVA_RUNTIME_COBWEB_ID;
+        String blockToolType = BlockTranslator.JAVA_RUNTIME_ID_TO_TOOL_TYPE.getOrDefault(blockId, "");
+        boolean canHarvestWithHand = BlockTranslator.JAVA_RUNTIME_ID_TO_CAN_HARVEST_WITH_HAND.get(blockId);
+        String toolType = "";
+        String toolTier = "";
+        boolean correctTool = false;
+        if (item instanceof ToolItemEntry) {
+            ToolItemEntry toolItem = (ToolItemEntry) item;
+            toolType = toolItem.getToolType();
+            toolTier = toolItem.getToolTier();
+            correctTool = correctTool(blockToolType, toolType);
+        }
+        int toolEfficiencyLevel = ItemUtils.getEnchantmentLevel(nbtData, "minecraft:efficiency");
+        int hasteLevel = player.getEffectCache().getEffectLevel(Effect.FASTER_DIG);
+        int miningFatigueLevel = player.getEffectCache().getEffectLevel(Effect.SLOWER_DIG);
+
+        // TODO implement these checks and material check if possible
+        //boolean insideOfWaterWithoutAquaAffinity = player.isInsideOfWater() &&
+        //        Optional.ofNullable(player.getInventory().getHelmet().getEnchantment(Enchantment.ID_WATER_WORKER))
+        //                .map(Enchantment::getLevel).map(l -> l >= 1).orElse(false);
+        //boolean outOfWaterButNotOnGround = (!player.isInsideOfWater()) && (!player.isOnGround());
+        return calculateBreakTime(blockHardness, toolTier, canHarvestWithHand, correctTool, toolType, isWoolBlock, isCobweb, toolEfficiencyLevel, hasteLevel, miningFatigueLevel);
+    }
+
+}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/ItemUtils.java b/connector/src/main/java/org/geysermc/connector/utils/ItemUtils.java
new file mode 100644
index 000000000..bb3cf0ed0
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/utils/ItemUtils.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.utils;
+
+import com.github.steveice10.opennbt.tag.builtin.*;
+
+public class ItemUtils {
+
+    public static int getEnchantmentLevel(CompoundTag itemNBTData, String enchantmentId) {
+        ListTag enchantments = (itemNBTData == null ? null : itemNBTData.get("Enchantments"));
+        if (enchantments != null) {
+            int enchantmentLevel = 0;
+            for (Tag tag : enchantments) {
+                CompoundTag enchantment = (CompoundTag) tag;
+                StringTag enchantId = enchantment.get("id");
+                if (enchantId.getValue().equals(enchantmentId)) {
+                    enchantmentLevel = (int) ((ShortTag) enchantment.get("lvl")).getValue();
+                }
+            }
+            return enchantmentLevel;
+        }
+        return 0;
+    }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java
index 9601b5819..be74ebb48 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java
@@ -40,6 +40,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
 
 import org.geysermc.connector.GeyserConnector;
 import org.geysermc.connector.network.translators.item.ItemEntry;
+import org.geysermc.connector.network.translators.item.ToolItemEntry;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
@@ -103,8 +104,28 @@ public class Toolbox {
         Iterator<Map.Entry<String, JsonNode>> iterator = items.fields();
         while (iterator.hasNext()) {
             Map.Entry<String, JsonNode> entry = iterator.next();
-            ITEM_ENTRIES.put(itemIndex, new ItemEntry(entry.getKey(), itemIndex,
-                    entry.getValue().get("bedrock_id").intValue(), entry.getValue().get("bedrock_data").intValue()));
+            if (entry.getValue().has("tool_type")) {
+                if (entry.getValue().has("tool_tier")) {
+                    ITEM_ENTRIES.put(itemIndex, new ToolItemEntry(
+                            entry.getKey(), itemIndex,
+                            entry.getValue().get("bedrock_id").intValue(),
+                            entry.getValue().get("bedrock_data").intValue(),
+                            entry.getValue().get("tool_type").textValue(),
+                            entry.getValue().get("tool_tier").textValue()));
+                } else {
+                    ITEM_ENTRIES.put(itemIndex, new ToolItemEntry(
+                            entry.getKey(), itemIndex,
+                            entry.getValue().get("bedrock_id").intValue(),
+                            entry.getValue().get("bedrock_data").intValue(),
+                            entry.getValue().get("tool_type").textValue(),
+                            ""));
+                }
+            } else {
+                ITEM_ENTRIES.put(itemIndex, new ItemEntry(
+                        entry.getKey(), itemIndex,
+                        entry.getValue().get("bedrock_id").intValue(),
+                        entry.getValue().get("bedrock_data").intValue()));
+            }
             itemIndex++;
         }