diff --git a/core/src/main/java/org/geysermc/geyser/inventory/GeyserItemStack.java b/core/src/main/java/org/geysermc/geyser/inventory/GeyserItemStack.java
index 732769287..91ee7c5cb 100644
--- a/core/src/main/java/org/geysermc/geyser/inventory/GeyserItemStack.java
+++ b/core/src/main/java/org/geysermc/geyser/inventory/GeyserItemStack.java
@@ -92,6 +92,9 @@ public class GeyserItemStack {
     }
 
     public ItemData getItemData(GeyserSession session) {
+        if (isEmpty()) {
+            return ItemData.AIR;
+        }
         ItemData.Builder itemData = ItemTranslator.translateToBedrock(session, javaId, amount, nbt);
         itemData.netId(getNetId());
         itemData.usingNetId(true);
diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java
index aec39cdd3..bb27f9dfa 100644
--- a/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java
+++ b/core/src/main/java/org/geysermc/geyser/registry/populator/BlockRegistryPopulator.java
@@ -35,7 +35,6 @@ import org.cloudburstmc.nbt.*;
 import org.cloudburstmc.protocol.bedrock.codec.v544.Bedrock_v544;
 import org.cloudburstmc.protocol.bedrock.codec.v560.Bedrock_v560;
 import org.cloudburstmc.protocol.bedrock.data.defintions.BlockDefinition;
-import org.cloudburstmc.protocol.common.SimpleDefinitionRegistry;
 import org.geysermc.geyser.GeyserImpl;
 import org.geysermc.geyser.level.block.BlockStateValues;
 import org.geysermc.geyser.level.physics.PistonBehavior;
@@ -86,6 +85,7 @@ public final class BlockRegistryPopulator {
             // New since 1.16.100 - find the block runtime ID by the order given to us in the block palette,
             // as we no longer send a block palette
             Object2ObjectMap<NbtMap, GeyserBedrockBlock> blockStateOrderedMap = new Object2ObjectOpenHashMap<>(blocksTag.size());
+            GeyserBedrockBlock[] bedrockRuntimeMap = new GeyserBedrockBlock[blocksTag.size()];
 
             int stateVersion = -1;
             for (int i = 0; i < blocksTag.size(); i++) {
@@ -97,6 +97,7 @@ public final class BlockRegistryPopulator {
                 }
                 GeyserBedrockBlock block = new GeyserBedrockBlock(i, tag);
                 blockStateOrderedMap.put(tag, block);
+                //bedrockRuntimeMap[i] = block; //TODO UNCOMMENT IF SOMETHING IS WRONG IN THE CREATIVE PALETTE
                 if (stateVersion == -1) {
                     stateVersion = tag.getInt("version");
                 }
@@ -112,7 +113,6 @@ public final class BlockRegistryPopulator {
             BiFunction<String, NbtMapBuilder, String> stateMapper = blockMappers.getOrDefault(palette.getKey(), emptyMapper);
 
             GeyserBedrockBlock[] javaToBedrockBlocks = new GeyserBedrockBlock[BLOCKS_JSON.size()];
-            SimpleDefinitionRegistry.Builder<BlockDefinition> registry = SimpleDefinitionRegistry.builder();
 
             Map<String, NbtMap> flowerPotBlocks = new Object2ObjectOpenHashMap<>();
             Map<NbtMap, BlockDefinition> itemFrames = new Object2ObjectOpenHashMap<>();
@@ -160,7 +160,7 @@ public final class BlockRegistryPopulator {
                 javaToBedrockBlocks[javaRuntimeId] = bedrockDefinition;
             }
 
-            Arrays.stream(javaToBedrockBlocks).distinct().forEach(registry::add);
+            Arrays.stream(javaToBedrockBlocks).distinct().forEach(block -> bedrockRuntimeMap[block.getRuntimeId()] = block);
 
             if (commandBlockDefinition == null) {
                 throw new AssertionError("Unable to find command block in palette");
@@ -191,10 +191,8 @@ public final class BlockRegistryPopulator {
                 }
             });
 
-            builder.bedrockBlockPalette(blocksTag);
-
             BlockRegistries.BLOCKS.register(palette.getKey().valueInt(), builder.blockStateVersion(stateVersion)
-                    .definitionRegistry(registry.build())
+                    .bedrockRuntimeMap(bedrockRuntimeMap)
                     .javaToBedrockBlocks(javaToBedrockBlocks)
                     .itemFrames(itemFrames)
                     .flowerPotBlocks(flowerPotBlocks)
diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/CreativeItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/CreativeItemRegistryPopulator.java
index 57ea15030..cc67ea88c 100644
--- a/core/src/main/java/org/geysermc/geyser/registry/populator/CreativeItemRegistryPopulator.java
+++ b/core/src/main/java/org/geysermc/geyser/registry/populator/CreativeItemRegistryPopulator.java
@@ -125,6 +125,6 @@ public class CreativeItemRegistryPopulator {
                 .damage(damage)
                 .count(count)
                 .tag(tag)
-                .blockDefinition(blockMappings.getDefinitionRegistry().getDefinition(bedrockBlockRuntimeId));
+                .blockDefinition(blockMappings.getDefinition(bedrockBlockRuntimeId));
     }
 }
diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java
index 8fe8723f3..ae6f8abe3 100644
--- a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java
+++ b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java
@@ -237,7 +237,7 @@ public class ItemRegistryPopulator {
                             // as indexed by Bedrock's block palette
                             // There are exceptions! But, ideally, the block ID override should take care of those.
                             NbtMapBuilder requiredBlockStatesBuilder = NbtMap.builder();
-                            String correctBedrockIdentifier = blockMappings.getBedrockBlockPalette().get(aValidBedrockBlockId).getString("name");
+                            String correctBedrockIdentifier = blockMappings.getDefinition(aValidBedrockBlockId).getState().getString("name");
                             boolean firstPass = true;
                             // Block states are all grouped together. In the mappings, we store the first block runtime ID in order,
                             // and the last, if relevant. We then iterate over all those values and get their Bedrock equivalents
@@ -279,11 +279,13 @@ public class ItemRegistryPopulator {
 
                             NbtMap requiredBlockStates = requiredBlockStatesBuilder.build();
                             if (bedrockBlock == null) {
-                                int i = -1;
                                 // We need to loop around again (we can't cache the block tags above) because Bedrock can include states that we don't have a pairing for
                                 // in it's "preferred" block state - I.E. the first matching block state in the list
-                                for (NbtMap blockTag : blockMappings.getBedrockBlockPalette()) {
-                                    i++;
+                                for (GeyserBedrockBlock block : blockMappings.getBedrockRuntimeMap()) {
+                                    if (block == null) {
+                                        continue;
+                                    }
+                                    NbtMap blockTag = block.getState();
                                     if (blockTag.getString("name").equals(correctBedrockIdentifier)) {
                                         NbtMap states = blockTag.getCompound("states");
                                         boolean valid = true;
@@ -295,7 +297,7 @@ public class ItemRegistryPopulator {
                                             }
                                         }
                                         if (valid) {
-                                            bedrockBlock = blockMappings.getDefinitionRegistry().getDefinition(i);
+                                            bedrockBlock = block;
                                             break;
                                         }
                                     }
@@ -314,7 +316,7 @@ public class ItemRegistryPopulator {
                                         break;
                                     }
 
-                                    NbtMap states = blockMappings.getBedrockBlockPalette().get(itemData.getBlockDefinition().getRuntimeId()).getCompound("states");
+                                    NbtMap states = ((GeyserBedrockBlock) itemData.getBlockDefinition()).getState().getCompound("states");
                                     boolean valid = true;
                                     for (Map.Entry<String, Object> nbtEntry : requiredBlockStates.entrySet()) {
                                         if (!states.get(nbtEntry.getKey()).equals(nbtEntry.getValue())) {
diff --git a/core/src/main/java/org/geysermc/geyser/registry/type/BlockMappings.java b/core/src/main/java/org/geysermc/geyser/registry/type/BlockMappings.java
index a565e7309..c93ad305c 100644
--- a/core/src/main/java/org/geysermc/geyser/registry/type/BlockMappings.java
+++ b/core/src/main/java/org/geysermc/geyser/registry/type/BlockMappings.java
@@ -27,7 +27,6 @@ package org.geysermc.geyser.registry.type;
 
 import lombok.Builder;
 import lombok.Value;
-import org.cloudburstmc.nbt.NbtList;
 import org.cloudburstmc.nbt.NbtMap;
 import org.cloudburstmc.protocol.bedrock.data.defintions.BlockDefinition;
 import org.cloudburstmc.protocol.common.DefinitionRegistry;
@@ -37,7 +36,7 @@ import java.util.Set;
 
 @Builder
 @Value
-public class BlockMappings {
+public class BlockMappings implements DefinitionRegistry<GeyserBedrockBlock> {
     GeyserBedrockBlock bedrockAir;
     BlockDefinition bedrockWater;
     BlockDefinition bedrockMovingBlock;
@@ -45,9 +44,8 @@ public class BlockMappings {
     int blockStateVersion;
 
     GeyserBedrockBlock[] javaToBedrockBlocks;
-    DefinitionRegistry<BlockDefinition> definitionRegistry;
 
-    NbtList<NbtMap> bedrockBlockPalette;
+    GeyserBedrockBlock[] bedrockRuntimeMap;
 
     BlockDefinition commandBlock;
 
@@ -78,4 +76,17 @@ public class BlockMappings {
 
         return false;
     }
+
+    @Override
+    public GeyserBedrockBlock getDefinition(int bedrockId) {
+        if (bedrockId < 0 || bedrockId >= this.bedrockRuntimeMap.length) {
+            return bedrockAir;
+        }
+        return bedrockRuntimeMap[bedrockId];
+    }
+
+    @Override
+    public boolean isRegistered(GeyserBedrockBlock bedrockBlock) {
+        return getDefinition(bedrockBlock.getRuntimeId()) == bedrockBlock;
+    }
 }
\ No newline at end of file
diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java
index 229649ded..ffd3ff209 100644
--- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java
+++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java
@@ -82,17 +82,14 @@ import lombok.experimental.Accessors;
 import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
 import org.checkerframework.common.value.qual.IntRange;
 import org.cloudburstmc.math.GenericMath;
-import org.cloudburstmc.math.vector.Vector2f;
-import org.cloudburstmc.math.vector.Vector2i;
-import org.cloudburstmc.math.vector.Vector3d;
-import org.cloudburstmc.math.vector.Vector3f;
-import org.cloudburstmc.math.vector.Vector3i;
+import org.cloudburstmc.math.vector.*;
 import org.cloudburstmc.nbt.NbtMap;
 import org.cloudburstmc.protocol.bedrock.BedrockServerSession;
 import org.cloudburstmc.protocol.bedrock.data.*;
 import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission;
 import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
 import org.cloudburstmc.protocol.bedrock.packet.*;
+import org.cloudburstmc.protocol.common.DefinitionRegistry;
 import org.cloudburstmc.protocol.common.util.OptionalBoolean;
 import org.geysermc.api.util.BedrockPlatform;
 import org.geysermc.api.util.InputMode;
@@ -145,15 +142,7 @@ import java.net.ConnectException;
 import java.net.InetSocketAddress;
 import java.nio.charset.StandardCharsets;
 import java.time.Instant;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.BitSet;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
+import java.util.*;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
@@ -1422,7 +1411,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
 
     private void startGame() {
         this.upstream.getCodecHelper().setItemDefinitions(this.itemMappings.getDefinitionRegistry());
-        this.upstream.getCodecHelper().setBlockDefinitions(this.blockMappings.getDefinitionRegistry());
+        this.upstream.getCodecHelper().setBlockDefinitions((DefinitionRegistry) this.blockMappings); //FIXME
 
         StartGamePacket startGamePacket = new StartGamePacket();
         startGamePacket.setUniqueEntityId(playerEntity.getGeyserId());
diff --git a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/PistonBlockEntity.java b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/PistonBlockEntity.java
index d954b2a8a..d61684c44 100644
--- a/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/PistonBlockEntity.java
+++ b/core/src/main/java/org/geysermc/geyser/translator/level/block/entity/PistonBlockEntity.java
@@ -780,7 +780,7 @@ public class PistonBlockEntity {
      */
     private NbtMap buildMovingBlockTag(Vector3i position, int javaId, Vector3i pistonPosition) {
         // Get Bedrock block state data
-        NbtMap movingBlock = session.getBlockMappings().getBedrockBlockPalette().get(session.getBlockMappings().getBedrockBlockId(javaId));
+        NbtMap movingBlock = session.getBlockMappings().getBedrockBlock(javaId).getState();
         NbtMapBuilder builder = NbtMap.builder()
                 .putString("id", "MovingBlock")
                 .putCompound("movingBlock", movingBlock)