diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java
index f4835863c..0c2183b34 100644
--- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java
+++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java
@@ -60,6 +60,7 @@ import org.geysermc.connector.entity.PlayerEntity;
 import org.geysermc.connector.inventory.PlayerInventory;
 import org.geysermc.connector.network.session.cache.*;
 import org.geysermc.connector.network.translators.Registry;
+import org.geysermc.connector.network.translators.block.BlockTranslator;
 import org.geysermc.connector.utils.ChunkUtils;
 import org.geysermc.connector.utils.Toolbox;
 
@@ -323,7 +324,7 @@ public class GeyserSession implements Player {
         // startGamePacket.setCurrentTick(0);
         startGamePacket.setEnchantmentSeed(0);
         startGamePacket.setMultiplayerCorrelationId("");
-        startGamePacket.setBlockPalette(Toolbox.BLOCKS);
+        startGamePacket.setBlockPalette(BlockTranslator.BLOCKS);
         startGamePacket.setItemEntries(Toolbox.ITEMS);
         startGamePacket.setVanillaVersion("*");
         // startGamePacket.setMovementServerAuthoritative(true);
diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java
index 3550e8dc3..5e94f3d9c 100644
--- a/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java
+++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java
@@ -33,7 +33,7 @@ import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
 import lombok.Getter;
 import org.geysermc.connector.network.session.GeyserSession;
 import org.geysermc.connector.network.translators.TranslatorsInit;
-import org.geysermc.connector.network.translators.block.BlockEntry;
+import org.geysermc.connector.network.translators.block.BlockTranslator;
 import org.geysermc.connector.world.chunk.ChunkPosition;
 
 import java.util.HashMap;
@@ -69,20 +69,19 @@ public class ChunkCache {
         }
     }
 
-    public BlockEntry getBlockAt(Position position) {
+    public BlockState getBlockAt(Position position) {
         ChunkPosition chunkPosition = new ChunkPosition(position.getX() >> 4, position.getZ() >> 4);
         if (!chunks.containsKey(chunkPosition))
-            return BlockEntry.AIR;
+            return BlockTranslator.AIR;
 
         Column column = chunks.get(chunkPosition);
         Chunk chunk = column.getChunks()[position.getY() >> 4];
         Position blockPosition = chunkPosition.getChunkBlock(position.getX(), position.getY(), position.getZ());
         if (chunk != null) {
-            BlockState blockState = chunk.get(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ());
-            return TranslatorsInit.getBlockTranslator().getBlockEntry(blockState);
+            return chunk.get(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ());
         }
 
-        return BlockEntry.AIR;
+        return BlockTranslator.AIR;
     }
 
     public void removeChunk(ChunkPosition position) {
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java b/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java
index bc14ba5d4..2abeebb8e 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/TranslatorsInit.java
@@ -27,7 +27,10 @@ package org.geysermc.connector.network.translators;
 
 import com.github.steveice10.mc.protocol.packet.ingame.server.*;
 import com.github.steveice10.mc.protocol.packet.ingame.server.entity.*;
-import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.*;
+import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerActionAckPacket;
+import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerHealthPacket;
+import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerPositionRotationPacket;
+import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerSetExperiencePacket;
 import com.github.steveice10.mc.protocol.packet.ingame.server.entity.spawn.*;
 import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerDisplayScoreboardPacket;
 import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerScoreboardObjectivePacket;
@@ -70,9 +73,6 @@ public class TranslatorsInit {
     @Getter
     private static ItemTranslator itemTranslator;
 
-    @Getter
-    private static BlockTranslator blockTranslator;
-
     @Getter
     private static InventoryTranslator inventoryTranslator = new GenericInventoryTranslator();
 
@@ -161,7 +161,7 @@ public class TranslatorsInit {
         Registry.registerBedrock(ShowCreditsPacket.class, new BedrockShowCreditsTranslator());
 
         itemTranslator = new ItemTranslator();
-        blockTranslator = new BlockTranslator();
+        BlockTranslator.init();
 
         registerInventoryTranslators();
     }
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockEntry.java b/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockEntry.java
deleted file mode 100644
index 10aa11c16..000000000
--- a/connector/src/main/java/org/geysermc/connector/network/translators/block/BlockEntry.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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.translators.block;
-
-import lombok.Getter;
-
-@Getter
-public class BlockEntry {
-
-    public static BlockEntry AIR = new BlockEntry("minecraft:air", 0, 0);
-
-    private final String javaIdentifier;
-    private final int javaId;
-    private final int bedrockRuntimeId;
-    private final boolean waterlogged;
-
-    public BlockEntry(String javaIdentifier, int javaId, int bedrockRuntimeId) {
-        this.javaIdentifier = javaIdentifier;
-        this.javaId = javaId;
-        this.bedrockRuntimeId = bedrockRuntimeId;
-        this.waterlogged = (javaIdentifier.contains("waterlogged=true")
-                || javaIdentifier.startsWith("minecraft:kelp")
-                || javaIdentifier.contains("seagrass")
-                || javaIdentifier.startsWith("minecraft:bubble_column"));
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        return obj == this || (obj instanceof BlockEntry && ((BlockEntry) obj).getBedrockRuntimeId() == this.getBedrockRuntimeId() && ((BlockEntry) obj).getJavaIdentifier().equals(this.getJavaIdentifier()));
-    }
-}
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 bbcf0f5da..f4753b8dd 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
@@ -25,21 +25,161 @@
 
 package org.geysermc.connector.network.translators.block;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
+import com.nukkitx.nbt.CompoundTagBuilder;
+import com.nukkitx.nbt.NbtUtils;
+import com.nukkitx.nbt.stream.NBTInputStream;
+import com.nukkitx.nbt.tag.CompoundTag;
+import com.nukkitx.nbt.tag.ListTag;
+import gnu.trove.map.TObjectIntMap;
+import gnu.trove.map.hash.TObjectIntHashMap;
+import it.unimi.dsi.fastutil.ints.*;
+import org.geysermc.connector.console.GeyserLogger;
 import org.geysermc.connector.utils.Toolbox;
 
-import java.util.HashMap;
-import java.util.Map;
+import java.io.InputStream;
+import java.util.*;
 
 public class BlockTranslator {
-    private final Map<String, BlockEntry> javaIdentifierMap = new HashMap<>();
+    public static final ListTag<CompoundTag> BLOCKS;
+    public static final BlockState AIR = new BlockState(0);
+    public static final int BEDROCK_WATER_ID;
 
-    public BlockEntry getBlockEntry(BlockState state) {
-        return Toolbox.BLOCK_ENTRIES.get(state.getId());
+    private static final Int2IntMap JAVA_TO_BEDROCK_BLOCK_MAP = new Int2IntOpenHashMap();
+    private static final Int2ObjectMap<BlockState> BEDROCK_TO_JAVA_BLOCK_MAP = new Int2ObjectOpenHashMap<>();
+    private static final IntSet WATERLOGGED = new IntOpenHashSet();
+
+    private static final int BLOCK_STATE_VERSION = 17760256;
+
+    static {
+        /* Load block palette */
+        InputStream stream = Toolbox.getResource("bedrock/runtime_block_states.dat");
+
+        ListTag<CompoundTag> blocksTag;
+        try (NBTInputStream nbtInputStream = NbtUtils.createNetworkReader(stream)) {
+            blocksTag = (ListTag<CompoundTag>) nbtInputStream.readTag();
+        } catch (Exception e) {
+            throw new AssertionError("Unable to get blocks from runtime block states", e);
+        }
+
+        Map<CompoundTag, CompoundTag> blockStateMap = new HashMap<>();
+
+        for (CompoundTag tag : blocksTag.getValue()) {
+            if (blockStateMap.putIfAbsent(tag.getAsCompound("block"), tag) != null) {
+                throw new AssertionError("Duplicate block states in Bedrock palette");
+            }
+        }
+
+        stream = Toolbox.getResource("mappings/blocks.json");
+        JsonNode blocks;
+        try {
+            blocks = Toolbox.JSON_MAPPER.readTree(stream);
+        } catch (Exception e) {
+            throw new AssertionError("Unable to load Java block mappings", e);
+        }
+        TObjectIntMap<CompoundTag> addedStatesMap = new TObjectIntHashMap<>(512, 0.5f, -1);
+        List<CompoundTag> paletteList = new ArrayList<>();
+
+        int waterRuntimeId = -1;
+        int javaRuntimeId = -1;
+        int bedrockRuntimeId = 0;
+        Iterator<Map.Entry<String, JsonNode>> blocksIterator = blocks.fields();
+        while (blocksIterator.hasNext()) {
+            javaRuntimeId++;
+            Map.Entry<String, JsonNode> entry = blocksIterator.next();
+            String javaId = entry.getKey();
+            CompoundTag blockTag = buildBedrockState(entry.getValue());
+
+            if ("minecraft:water[level=0]".equals(javaId)) {
+                waterRuntimeId = bedrockRuntimeId;
+            }
+            boolean waterlogged = entry.getValue().has("waterlogged") && entry.getValue().get("waterlogged").booleanValue();
+
+            if (waterlogged) {
+                BEDROCK_TO_JAVA_BLOCK_MAP.putIfAbsent(bedrockRuntimeId | 1 << 31, new BlockState(javaRuntimeId));
+                WATERLOGGED.add(javaRuntimeId);
+            } else {
+                BEDROCK_TO_JAVA_BLOCK_MAP.putIfAbsent(bedrockRuntimeId, new BlockState(javaRuntimeId));
+            }
+
+            CompoundTag runtimeTag = blockStateMap.remove(blockTag);
+            if (runtimeTag != null) {
+                addedStatesMap.put(blockTag, bedrockRuntimeId);
+                paletteList.add(runtimeTag);
+            } else {
+                int duplicateRuntimeId = addedStatesMap.get(blockTag);
+                if (duplicateRuntimeId == -1) {
+                    GeyserLogger.DEFAULT.debug("Mapping " + javaId + " was not found for bedrock edition!");
+                } else {
+                    JAVA_TO_BEDROCK_BLOCK_MAP.put(javaRuntimeId, duplicateRuntimeId);
+                }
+                continue;
+            }
+            JAVA_TO_BEDROCK_BLOCK_MAP.put(javaRuntimeId, bedrockRuntimeId);
+
+            bedrockRuntimeId++;
+        }
+
+        if (waterRuntimeId == -1) {
+            throw new AssertionError("Unable to find water in palette");
+        }
+        BEDROCK_WATER_ID = waterRuntimeId;
+
+        paletteList.addAll(blockStateMap.values()); // Add any missing mappings that could crash the client
+
+        BLOCKS = new ListTag<>("", CompoundTag.class, paletteList);
     }
 
-    public BlockEntry getBlockEntry(String javaIdentifier) {
-        return javaIdentifierMap.computeIfAbsent(javaIdentifier, key -> Toolbox.BLOCK_ENTRIES.values()
-                .stream().filter(blockEntry -> blockEntry.getJavaIdentifier().equals(key)).findFirst().orElse(null));
+    private BlockTranslator() {
+    }
+
+    public static void init() {
+        // no-op
+    }
+
+    private static CompoundTag buildBedrockState(JsonNode node) {
+        CompoundTagBuilder tagBuilder = CompoundTag.builder();
+        tagBuilder.stringTag("name", node.get("bedrock_identifier").textValue())
+                .intTag("version", BlockTranslator.BLOCK_STATE_VERSION);
+
+        CompoundTagBuilder statesBuilder = CompoundTag.builder();
+
+        // check for states
+        if (node.has("bedrock_states")) {
+            Iterator<Map.Entry<String, JsonNode>> statesIterator = node.get("bedrock_states").fields();
+
+            while (statesIterator.hasNext()) {
+                Map.Entry<String, JsonNode> stateEntry = statesIterator.next();
+                JsonNode stateValue = stateEntry.getValue();
+                switch (stateValue.getNodeType()) {
+                    case BOOLEAN:
+                        statesBuilder.booleanTag(stateEntry.getKey(), stateValue.booleanValue());
+                        continue;
+                    case STRING:
+                        statesBuilder.stringTag(stateEntry.getKey(), stateValue.textValue());
+                        continue;
+                    case NUMBER:
+                        statesBuilder.intTag(stateEntry.getKey(), stateValue.intValue());
+                }
+            }
+        }
+        return tagBuilder.tag(statesBuilder.build("states")).build("block");
+    }
+
+    public static int getBedrockBlockId(BlockState state) {
+        return JAVA_TO_BEDROCK_BLOCK_MAP.get(state.getId());
+    }
+
+    public static BlockState getJavaBlockState(int bedrockId) {
+        return BEDROCK_TO_JAVA_BLOCK_MAP.get(bedrockId);
+    }
+
+    public static boolean isWaterlogged(BlockState state) {
+        return WATERLOGGED.contains(state.getId());
+    }
+
+    public static BlockState getJavaWaterloggedState(int bedrockId) {
+        return BEDROCK_TO_JAVA_BLOCK_MAP.get(1 << 31 | bedrockId);
     }
 }
diff --git a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java
index 4ceceb7e3..6c55c5ea8 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java
@@ -35,9 +35,11 @@ import com.nukkitx.protocol.bedrock.packet.NetworkChunkPublisherUpdatePacket;
 import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
 import org.geysermc.connector.network.session.GeyserSession;
 import org.geysermc.connector.network.translators.TranslatorsInit;
-import org.geysermc.connector.network.translators.block.BlockEntry;
+import org.geysermc.connector.network.translators.block.BlockTranslator;
 import org.geysermc.connector.world.chunk.ChunkSection;
 
+import static org.geysermc.connector.network.translators.block.BlockTranslator.BEDROCK_WATER_ID;
+
 public class ChunkUtils {
     public static ChunkData translateToBedrock(Column column) {
         ChunkData chunkData = new ChunkData();
@@ -59,14 +61,12 @@ public class ChunkUtils {
                 for (int y = 0; y < 16; y++) {
                     for (int z = 0; z < 16; z++) {
                         BlockState blockState = chunk.get(x, y, z);
-                        BlockEntry block = TranslatorsInit.getBlockTranslator().getBlockEntry(blockState);
+                        int id = BlockTranslator.getBedrockBlockId(blockState);
 
-                        section.getBlockStorageArray()[0].setFullBlock(ChunkSection.blockPosition(x, y, z),
-                                block.getBedrockRuntimeId());
+                        section.getBlockStorageArray()[0].setFullBlock(ChunkSection.blockPosition(x, y, z), id);
 
-                        if (block.isWaterlogged()) {
-                            BlockEntry water = TranslatorsInit.getBlockTranslator().getBlockEntry("minecraft:water[level=0]");
-                            section.getBlockStorageArray()[1].setFullBlock(ChunkSection.blockPosition(x, y, z), water.getBedrockRuntimeId());
+                        if (BlockTranslator.isWaterlogged(blockState)) {
+                            section.getBlockStorageArray()[1].setFullBlock(ChunkSection.blockPosition(x, y, z), BEDROCK_WATER_ID);
                         }
                     }
                 }
@@ -76,22 +76,21 @@ public class ChunkUtils {
     }
 
     public static void updateBlock(GeyserSession session, BlockState blockState, Position position) {
-        BlockEntry blockEntry = TranslatorsInit.getBlockTranslator().getBlockEntry(blockState);
+        int blockId = BlockTranslator.getBedrockBlockId(blockState);
         Vector3i pos = Vector3i.from(position.getX(), position.getY(), position.getZ());
 
         UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
         updateBlockPacket.setDataLayer(0);
         updateBlockPacket.setBlockPosition(pos);
-        updateBlockPacket.setRuntimeId(blockEntry.getBedrockRuntimeId());
+        updateBlockPacket.setRuntimeId(blockId);
         updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
         session.getUpstream().sendPacket(updateBlockPacket);
 
         UpdateBlockPacket waterPacket = new UpdateBlockPacket();
         waterPacket.setDataLayer(1);
         waterPacket.setBlockPosition(pos);
-        if (blockEntry.isWaterlogged()) {
-            BlockEntry water = TranslatorsInit.getBlockTranslator().getBlockEntry("minecraft:water[level=0]");
-            waterPacket.setRuntimeId(water.getBedrockRuntimeId());
+        if (BlockTranslator.isWaterlogged(blockState)) {
+            waterPacket.setRuntimeId(BEDROCK_WATER_ID);
         } else {
             waterPacket.setRuntimeId(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 d2beb8074..5dba02b69 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java
@@ -25,20 +25,18 @@
 
 package org.geysermc.connector.utils;
 
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.nukkitx.nbt.NbtUtils;
 import com.nukkitx.nbt.stream.NBTInputStream;
 import com.nukkitx.nbt.tag.CompoundTag;
-import com.nukkitx.nbt.tag.ListTag;
-import com.nukkitx.nbt.tag.Tag;
-import com.nukkitx.protocol.bedrock.packet.BiomeDefinitionListPacket;
 import com.nukkitx.protocol.bedrock.packet.StartGamePacket;
-
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
 import org.geysermc.connector.GeyserConnector;
 import org.geysermc.connector.console.GeyserLogger;
-import org.geysermc.connector.network.translators.block.BlockEntry;
 import org.geysermc.connector.network.translators.item.ItemEntry;
 
 import java.io.InputStream;
@@ -46,14 +44,15 @@ import java.util.*;
 
 public class Toolbox {
 
+    public static final ObjectMapper JSON_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);
+    public static final CompoundTag BIOMES;
+
     public static final Collection<StartGamePacket.ItemEntry> ITEMS = new ArrayList<>();
-    public static ListTag<CompoundTag> BLOCKS;
-    public static CompoundTag BIOMES;
 
     public static final Int2ObjectMap<ItemEntry> ITEM_ENTRIES = new Int2ObjectOpenHashMap<>();
-    public static final Int2ObjectMap<BlockEntry> BLOCK_ENTRIES = new Int2ObjectOpenHashMap<>();
 
-    public static void init() {
+    static {
+        /* Load biomes */
         InputStream biomestream = GeyserConnector.class.getClassLoader().getResourceAsStream("bedrock/biome_definitions.dat");
         if (biomestream == null) {
             throw new AssertionError("Unable to find bedrock/biome_definitions.dat");
@@ -69,99 +68,51 @@ public class Toolbox {
             throw new AssertionError(ex);
         }
 
-        InputStream stream = GeyserConnector.class.getClassLoader().getResourceAsStream("bedrock/runtime_block_states.dat");
-        if (stream == null) {
-            throw new AssertionError("Unable to find bedrock/runtime_block_states.dat");
-        }
+        /* Load item palette */
+        InputStream stream = getResource("bedrock/items.json");
 
-        ListTag<CompoundTag> blocksTag;
+        TypeReference<List<JsonNode>> itemEntriesType = new TypeReference<List<JsonNode>>() {
+        };
 
-        NBTInputStream nbtInputStream = NbtUtils.createNetworkReader(stream);
+        List<JsonNode> itemEntries;
         try {
-            blocksTag = (ListTag<CompoundTag>) nbtInputStream.readTag();
-            nbtInputStream.close();
-        } catch (Exception ex) {
-            GeyserLogger.DEFAULT.warning("Failed to get blocks from runtime block states, please report this error!");
-            throw new AssertionError(ex);
-        }
-
-        BLOCKS = blocksTag;
-
-        InputStream stream2 = Toolbox.class.getClassLoader().getResourceAsStream("bedrock/items.json");
-        if (stream2 == null) {
-            throw new AssertionError("Items Table not found");
-        }
-
-        ObjectMapper startGameItemMapper = new ObjectMapper();
-        List<Map> startGameItems = new ArrayList<>();
-        try {
-            startGameItems = startGameItemMapper.readValue(stream2, ArrayList.class);
+            itemEntries = JSON_MAPPER.readValue(stream, itemEntriesType);
         } catch (Exception e) {
-            e.printStackTrace();
+            throw new AssertionError("Unable to load Bedrock runtime item IDs", e);
         }
 
-        for (Map entry : startGameItems) {
-            ITEMS.add(new StartGamePacket.ItemEntry((String) entry.get("name"), (short) ((int) entry.get("id"))));
+        for (JsonNode entry : itemEntries) {
+            ITEMS.add(new StartGamePacket.ItemEntry(entry.get("name").textValue(), (short) entry.get("id").intValue()));
         }
 
-        InputStream itemStream = Toolbox.class.getClassLoader().getResourceAsStream("mappings/items.json");
-        ObjectMapper itemMapper = new ObjectMapper();
-        Map<String, Map<String, Object>> items = new HashMap<>();
+        stream = getResource("mappings/items.json");
 
+        JsonNode items;
         try {
-            items = itemMapper.readValue(itemStream, LinkedHashMap.class);
-        } catch (Exception ex) {
-            ex.printStackTrace();
+            items = JSON_MAPPER.readTree(stream);
+        } catch (Exception e) {
+            throw new AssertionError("Unable to load Java runtime item IDs", e);
         }
 
         int itemIndex = 0;
-        for (Map.Entry<String, Map<String, Object>> itemEntry : items.entrySet()) {
-            ITEM_ENTRIES.put(itemIndex, new ItemEntry(itemEntry.getKey(), itemIndex, (int) itemEntry.getValue().get("bedrock_id"), (int) itemEntry.getValue().get("bedrock_data")));
+        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()));
             itemIndex++;
         }
+    }
 
-        InputStream blockStream = Toolbox.class.getClassLoader().getResourceAsStream("mappings/blocks.json");
-        ObjectMapper blockMapper = new ObjectMapper();
-        Map<String, Map<String, Object>> blocks = new HashMap<>();
-
-        try {
-            blocks = blockMapper.readValue(blockStream, LinkedHashMap.class);
-        } catch (Exception ex) {
-            ex.printStackTrace();
+    public static InputStream getResource(String resource) {
+        InputStream stream = Toolbox.class.getClassLoader().getResourceAsStream(resource);
+        if (stream == null) {
+            throw new AssertionError("Unable to find resource: " + resource);
         }
+        return stream;
+    }
 
-        int javaIndex = -1;
-        javaLoop:
-        for (Map.Entry<String, Map<String, Object>> javaEntry : blocks.entrySet()) {
-            javaIndex++;
-            String wantedIdentifier = (String) javaEntry.getValue().get("bedrock_identifier");
-            Map<String, Object> wantedStates = (Map<String, Object>) javaEntry.getValue().get("bedrock_states");
-
-            int bedrockIndex = -1;
-            bedrockLoop:
-            for (CompoundTag bedrockEntry : BLOCKS.getValue()) {
-                bedrockIndex++;
-                CompoundTag blockTag = bedrockEntry.getAsCompound("block");
-                if (blockTag.getAsString("name").equals(wantedIdentifier)) {
-                    if (wantedStates != null) {
-                        Map<String, Tag<?>> bedrockStates = blockTag.getAsCompound("states").getValue();
-                        for (Map.Entry<String, Object> stateEntry : wantedStates.entrySet()) {
-                            Tag<?> bedrockStateTag = bedrockStates.get(stateEntry.getKey());
-                            if (bedrockStateTag == null)
-                                continue bedrockLoop;
-                            Object bedrockStateValue = bedrockStateTag.getValue();
-                            if (bedrockStateValue instanceof Byte)
-                                bedrockStateValue = ((Byte) bedrockStateValue) != 0;
-                            if (!stateEntry.getValue().equals(bedrockStateValue))
-                                continue bedrockLoop;
-                        }
-                    }
-                    BlockEntry blockEntry = new BlockEntry(javaEntry.getKey(), javaIndex, bedrockIndex);
-                    BLOCK_ENTRIES.put(javaIndex, blockEntry);
-                    continue javaLoop;
-                }
-            }
-            GeyserLogger.DEFAULT.debug("Mapping " + javaEntry.getKey() + " was not found for bedrock edition!");
-        }
+    public static void init() {
+        // no-op
     }
 }
\ No newline at end of file
diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings
index 1ad8a1941..278c73449 160000
--- a/connector/src/main/resources/mappings
+++ b/connector/src/main/resources/mappings
@@ -1 +1 @@
-Subproject commit 1ad8a19417391710d34d72214e3aa430f84740cd
+Subproject commit 278c73449aeeb4064c7513a68f98a49a5f463f0a