diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
index 3ce32ab75e..4ecf846081 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
@@ -20,11 +20,14 @@ import org.bukkit.Particle;
 import org.bukkit.Registry;
 import org.bukkit.attribute.Attribute;
 import org.bukkit.block.Biome;
+import org.bukkit.block.BlockType;
+import org.bukkit.craftbukkit.block.CraftBlockType;
 import org.bukkit.craftbukkit.damage.CraftDamageType;
 import org.bukkit.craftbukkit.enchantments.CraftEnchantment;
 import org.bukkit.craftbukkit.entity.CraftWolf;
 import org.bukkit.craftbukkit.generator.structure.CraftStructure;
 import org.bukkit.craftbukkit.generator.structure.CraftStructureType;
+import org.bukkit.craftbukkit.inventory.CraftItemType;
 import org.bukkit.craftbukkit.inventory.trim.CraftTrimMaterial;
 import org.bukkit.craftbukkit.inventory.trim.CraftTrimPattern;
 import org.bukkit.craftbukkit.legacy.FieldRename;
@@ -38,6 +41,7 @@ import org.bukkit.entity.EntityType;
 import org.bukkit.entity.Wolf;
 import org.bukkit.generator.structure.Structure;
 import org.bukkit.generator.structure.StructureType;
+import org.bukkit.inventory.ItemType;
 import org.bukkit.inventory.meta.trim.TrimMaterial;
 import org.bukkit.inventory.meta.trim.TrimPattern;
 import org.bukkit.potion.PotionEffectType;
@@ -115,7 +119,7 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
      * @param registryHolder the minecraft registry holder
      * @return the bukkit registry of the provided class
      */
-    public static <B extends Keyed> Registry<?> createRegistry(Class<B> bukkitClass, IRegistryCustom registryHolder) {
+    public static <B extends Keyed> Registry<?> createRegistry(Class<? super B> bukkitClass, IRegistryCustom registryHolder) {
         if (bukkitClass == Enchantment.class) {
             return new CraftRegistry<>(Enchantment.class, registryHolder.registryOrThrow(Registries.ENCHANTMENT), CraftEnchantment::new, FieldRename.ENCHANTMENT_RENAME);
         }
@@ -146,6 +150,12 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
         if (bukkitClass == Wolf.Variant.class) {
             return new CraftRegistry<>(Wolf.Variant.class, registryHolder.registryOrThrow(Registries.WOLF_VARIANT), CraftWolf.CraftVariant::new, NONE);
         }
+        if (bukkitClass == BlockType.class) {
+            return new CraftRegistry<>(BlockType.class, registryHolder.registryOrThrow(Registries.BLOCK), CraftBlockType::new, NONE);
+        }
+        if (bukkitClass == ItemType.class) {
+            return new CraftRegistry<>(ItemType.class, registryHolder.registryOrThrow(Registries.ITEM), CraftItemType::new, NONE);
+        }
 
         return null;
     }
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index d1d13683a4..8a2bcf7123 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -121,6 +121,7 @@ import org.bukkit.ChatColor;
 import org.bukkit.GameMode;
 import org.bukkit.Keyed;
 import org.bukkit.Location;
+import org.bukkit.Material;
 import org.bukkit.NamespacedKey;
 import org.bukkit.OfflinePlayer;
 import org.bukkit.Registry;
@@ -2364,14 +2365,14 @@ public final class CraftServer implements Server {
     public BlockData createBlockData(String data) throws IllegalArgumentException {
         Preconditions.checkArgument(data != null, "data cannot be null");
 
-        return createBlockData(null, data);
+        return createBlockData((Material) null, data);
     }
 
     @Override
     public BlockData createBlockData(org.bukkit.Material material, String data) {
         Preconditions.checkArgument(material != null || data != null, "Must provide one of material or data");
 
-        return CraftBlockData.newData(material, data);
+        return CraftBlockData.newData((material != null) ? material.asBlockType() : null, data);
     }
 
     @Override
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockType.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockType.java
index 22f8b29e7a..b335d07068 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockType.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftBlockType.java
@@ -1,10 +1,42 @@
 package org.bukkit.craftbukkit.block;
 
+import com.google.common.base.Preconditions;
+import java.util.function.Consumer;
+import net.minecraft.core.BlockPosition;
+import net.minecraft.core.registries.Registries;
+import net.minecraft.world.EnumHand;
+import net.minecraft.world.entity.player.EntityHuman;
+import net.minecraft.world.item.Item;
+import net.minecraft.world.item.Items;
+import net.minecraft.world.level.BlockAccessAir;
 import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.BlockFalling;
+import net.minecraft.world.level.block.BlockFire;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.block.state.BlockBase;
+import net.minecraft.world.level.block.state.IBlockData;
+import net.minecraft.world.phys.MovingObjectPositionBlock;
 import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.Registry;
+import org.bukkit.World;
+import org.bukkit.block.BlockType;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.craftbukkit.CraftRegistry;
+import org.bukkit.craftbukkit.CraftWorld;
+import org.bukkit.craftbukkit.block.data.CraftBlockData;
+import org.bukkit.craftbukkit.inventory.CraftItemType;
 import org.bukkit.craftbukkit.util.CraftMagicNumbers;
+import org.bukkit.craftbukkit.util.Handleable;
+import org.bukkit.inventory.ItemType;
+import org.jetbrains.annotations.NotNull;
 
-public class CraftBlockType {
+public class CraftBlockType<B extends BlockData> implements BlockType.Typed<B>, Handleable<Block> {
+
+    private final NamespacedKey key;
+    private final Block block;
+    private final Class<B> blockDataClass;
+    private final boolean interactable;
 
     public static Material minecraftToBukkit(Block block) {
         return CraftMagicNumbers.getMaterial(block);
@@ -13,4 +45,184 @@ public class CraftBlockType {
     public static Block bukkitToMinecraft(Material material) {
         return CraftMagicNumbers.getBlock(material);
     }
+
+    public static BlockType minecraftToBukkitNew(Block minecraft) {
+        return CraftRegistry.minecraftToBukkit(minecraft, Registries.BLOCK, Registry.BLOCK);
+    }
+
+    public static Block bukkitToMinecraftNew(BlockType bukkit) {
+        return CraftRegistry.bukkitToMinecraft(bukkit);
+    }
+
+    private static boolean hasMethod(Class<?> clazz, String methodName, Class<?>... params) {
+        boolean hasMethod;
+        try {
+            hasMethod = clazz.getDeclaredMethod(methodName, params) != null;
+        } catch (NoSuchMethodException ex) {
+            hasMethod = false;
+        }
+
+        return hasMethod;
+    }
+
+    private static boolean isInteractable(Block block) {
+        Class<?> clazz = block.getClass();
+
+        boolean hasMethod = hasMethod(clazz, "useWithoutItem", IBlockData.class, net.minecraft.world.level.World.class, BlockPosition.class, EntityHuman.class, MovingObjectPositionBlock.class)
+                || hasMethod(clazz, "useItemOn", net.minecraft.world.item.ItemStack.class, IBlockData.class, net.minecraft.world.level.World.class, BlockPosition.class, EntityHuman.class, EnumHand.class, MovingObjectPositionBlock.class);
+
+        if (!hasMethod && clazz.getSuperclass() != BlockBase.class) {
+            clazz = clazz.getSuperclass();
+
+            hasMethod = hasMethod(clazz, "useWithoutItem", IBlockData.class, net.minecraft.world.level.World.class, BlockPosition.class, EntityHuman.class, MovingObjectPositionBlock.class)
+                    || hasMethod(clazz, "useItemOn", net.minecraft.world.item.ItemStack.class, IBlockData.class, net.minecraft.world.level.World.class, BlockPosition.class, EntityHuman.class, EnumHand.class, MovingObjectPositionBlock.class);
+        }
+
+        return hasMethod;
+    }
+
+    public CraftBlockType(NamespacedKey key, Block block) {
+        this.key = key;
+        this.block = block;
+        this.blockDataClass = (Class<B>) CraftBlockData.fromData(block.defaultBlockState()).getClass().getInterfaces()[0];
+        this.interactable = isInteractable(block);
+    }
+
+    @Override
+    public Block getHandle() {
+        return block;
+    }
+
+    @NotNull
+    @Override
+    public Typed<BlockData> typed() {
+        return this.typed(BlockData.class);
+    }
+
+    @NotNull
+    @Override
+    @SuppressWarnings("unchecked")
+    public <Other extends BlockData> Typed<Other> typed(@NotNull Class<Other> blockDataType) {
+        if (blockDataType.isAssignableFrom(this.blockDataClass)) return (Typed<Other>) this;
+        throw new IllegalArgumentException("Cannot type block type " + this.key.toString() + " to blockdata type " + blockDataType.getSimpleName());
+    }
+
+    @Override
+    public boolean hasItemType() {
+        if (this == AIR) {
+            return true;
+        }
+
+        return block.asItem() != Items.AIR;
+    }
+
+    @NotNull
+    @Override
+    public ItemType getItemType() {
+        if (this == AIR) {
+            return ItemType.AIR;
+        }
+
+        Item item = block.asItem();
+        Preconditions.checkArgument(item != Items.AIR, "The block type %s has no corresponding item type", getKey());
+        return CraftItemType.minecraftToBukkitNew(item);
+    }
+
+    @Override
+    public Class<B> getBlockDataClass() {
+        return blockDataClass;
+    }
+
+    @Override
+    public B createBlockData() {
+        return createBlockData((String) null);
+    }
+
+    @Override
+    public B createBlockData(Consumer<? super B> consumer) {
+        B data = createBlockData();
+
+        if (consumer != null) {
+            consumer.accept(data);
+        }
+
+        return data;
+    }
+
+    @Override
+    public B createBlockData(String data) {
+        return (B) CraftBlockData.newData(this, data);
+    }
+
+    @Override
+    public boolean isSolid() {
+        return block.defaultBlockState().blocksMotion();
+    }
+
+    @Override
+    public boolean isAir() {
+        return block.defaultBlockState().isAir();
+    }
+
+    @Override
+    public boolean isEnabledByFeature(@NotNull World world) {
+        Preconditions.checkNotNull(world, "World cannot be null");
+        return getHandle().isEnabled(((CraftWorld) world).getHandle().enabledFeatures());
+    }
+
+    @Override
+    public boolean isFlammable() {
+        return block.defaultBlockState().ignitedByLava();
+    }
+
+    @Override
+    public boolean isBurnable() {
+        return ((BlockFire) Blocks.FIRE).igniteOdds.getOrDefault(block, 0) > 0;
+    }
+
+    @Override
+    public boolean isOccluding() {
+        return block.defaultBlockState().isRedstoneConductor(BlockAccessAir.INSTANCE, BlockPosition.ZERO);
+    }
+
+    @Override
+    public boolean hasGravity() {
+        return block instanceof BlockFalling;
+    }
+
+    @Override
+    public boolean isInteractable() {
+        return interactable;
+    }
+
+    @Override
+    public float getHardness() {
+        return block.defaultBlockState().destroySpeed;
+    }
+
+    @Override
+    public float getBlastResistance() {
+        return block.getExplosionResistance();
+    }
+
+    @Override
+    public float getSlipperiness() {
+        return block.getFriction();
+    }
+
+    @NotNull
+    @Override
+    public String getTranslationKey() {
+        return block.getDescriptionId();
+    }
+
+    @Override
+    public NamespacedKey getKey() {
+        return key;
+    }
+
+    @Override
+    public Material asMaterial() {
+        return Registry.MATERIAL.get(this.key);
+    }
 }
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/data/CraftBlockData.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/data/CraftBlockData.java
index 34a8e54fc0..75c3c1214b 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/data/CraftBlockData.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/data/CraftBlockData.java
@@ -31,6 +31,7 @@ import org.bukkit.SoundGroup;
 import org.bukkit.block.BlockFace;
 import org.bukkit.block.BlockState;
 import org.bukkit.block.BlockSupport;
+import org.bukkit.block.BlockType;
 import org.bukkit.block.PistonMoveReaction;
 import org.bukkit.block.data.BlockData;
 import org.bukkit.block.structure.Mirror;
@@ -540,11 +541,9 @@ public class CraftBlockData implements BlockData {
         Preconditions.checkState(MAP.put(nms, bukkit) == null, "Duplicate mapping %s->%s", nms, bukkit);
     }
 
-    public static CraftBlockData newData(Material material, String data) {
-        Preconditions.checkArgument(material == null || material.isBlock(), "Cannot get data for not block %s", material);
-
+    public static CraftBlockData newData(BlockType blockType, String data) {
         IBlockData blockData;
-        Block block = CraftBlockType.bukkitToMinecraft(material);
+        Block block = blockType == null ? null : ((CraftBlockType<?>) blockType).getHandle();
         Map<IBlockState<?>, Comparable<?>> parsed = null;
 
         // Data provided, use it
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java
index d2ef2a7218..f4d5b624f9 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java
@@ -1,10 +1,44 @@
 package org.bukkit.craftbukkit.inventory;
 
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
+import java.util.function.Consumer;
+import net.minecraft.core.component.DataComponents;
+import net.minecraft.core.registries.Registries;
 import net.minecraft.world.item.Item;
+import net.minecraft.world.item.ItemBlock;
+import net.minecraft.world.item.ItemRecord;
+import net.minecraft.world.item.component.ItemAttributeModifiers;
+import net.minecraft.world.level.block.entity.TileEntityFurnace;
 import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.Registry;
+import org.bukkit.World;
+import org.bukkit.attribute.Attribute;
+import org.bukkit.attribute.AttributeModifier;
+import org.bukkit.block.BlockType;
+import org.bukkit.craftbukkit.CraftEquipmentSlot;
+import org.bukkit.craftbukkit.CraftRegistry;
+import org.bukkit.craftbukkit.CraftWorld;
+import org.bukkit.craftbukkit.attribute.CraftAttribute;
+import org.bukkit.craftbukkit.attribute.CraftAttributeInstance;
+import org.bukkit.craftbukkit.block.CraftBlockType;
 import org.bukkit.craftbukkit.util.CraftMagicNumbers;
+import org.bukkit.craftbukkit.util.Handleable;
+import org.bukkit.inventory.CreativeCategory;
+import org.bukkit.inventory.EquipmentSlot;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.ItemType;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
-public class CraftItemType {
+public class CraftItemType<M extends ItemMeta> implements ItemType.Typed<M>, Handleable<Item> {
+
+    private final NamespacedKey key;
+    private final Item item;
+    private final Class<M> itemMetaClass;
 
     public static Material minecraftToBukkit(Item item) {
         return CraftMagicNumbers.getMaterial(item);
@@ -13,4 +47,183 @@ public class CraftItemType {
     public static Item bukkitToMinecraft(Material material) {
         return CraftMagicNumbers.getItem(material);
     }
+
+    public static ItemType minecraftToBukkitNew(Item minecraft) {
+        return CraftRegistry.minecraftToBukkit(minecraft, Registries.ITEM, Registry.ITEM);
+    }
+
+    public static Item bukkitToMinecraftNew(ItemType bukkit) {
+        return CraftRegistry.bukkitToMinecraft(bukkit);
+    }
+
+    public CraftItemType(NamespacedKey key, Item item) {
+        this.key = key;
+        this.item = item;
+        this.itemMetaClass = getItemMetaClass(item);
+    }
+
+    // Cursed, this should be refactored when possible
+    private Class<M> getItemMetaClass(Item item) {
+        ItemMeta meta = new ItemStack(asMaterial()).getItemMeta();
+        if (meta != null) {
+            if (CraftMetaEntityTag.class != meta.getClass() && CraftMetaArmorStand.class != meta.getClass()) {
+                return (Class<M>) meta.getClass().getInterfaces()[0];
+            }
+        }
+        return (Class<M>) ItemMeta.class;
+    }
+
+    @NotNull
+    @Override
+    public Typed<ItemMeta> typed() {
+        return this.typed(ItemMeta.class);
+    }
+
+    @NotNull
+    @Override
+    @SuppressWarnings("unchecked")
+    public <Other extends ItemMeta> Typed<Other> typed(@NotNull final Class<Other> itemMetaType) {
+        if (itemMetaType.isAssignableFrom(this.itemMetaClass)) return (Typed<Other>) this;
+
+        throw new IllegalArgumentException("Cannot type item type " + this.key.toString() + " to meta type " + itemMetaType.getSimpleName());
+    }
+
+    @NotNull
+    @Override
+    public ItemStack createItemStack() {
+        return this.createItemStack(1, null);
+    }
+
+    @NotNull
+    @Override
+    public ItemStack createItemStack(final int amount) {
+        return this.createItemStack(amount, null);
+    }
+
+    @NotNull
+    @Override
+    public ItemStack createItemStack(Consumer<? super M> metaConfigurator) {
+        return this.createItemStack(1, metaConfigurator);
+    }
+
+    @NotNull
+    @Override
+    public ItemStack createItemStack(final int amount, @Nullable final Consumer<? super M> metaConfigurator) {
+        final ItemStack itemStack = new ItemStack(this.asMaterial(), amount);
+        if (metaConfigurator != null) {
+            final ItemMeta itemMeta = itemStack.getItemMeta();
+            metaConfigurator.accept((M) itemMeta);
+            itemStack.setItemMeta(itemMeta);
+        }
+        return itemStack;
+    }
+
+    @Override
+    public Item getHandle() {
+        return item;
+    }
+
+    @Override
+    public boolean hasBlockType() {
+        return item instanceof ItemBlock;
+    }
+
+    @NotNull
+    @Override
+    public BlockType getBlockType() {
+        if (!(item instanceof ItemBlock block)) {
+            throw new IllegalStateException("The item type " + getKey() + " has no corresponding block type");
+        }
+
+        return CraftBlockType.minecraftToBukkitNew(block.getBlock());
+    }
+
+    @Override
+    public Class<M> getItemMetaClass() {
+        if (this == ItemType.AIR) {
+            throw new UnsupportedOperationException("Air does not have ItemMeta");
+        }
+        return itemMetaClass;
+    }
+
+    @Override
+    public int getMaxStackSize() {
+        // Based of the material enum air is only 0, in PerMaterialTest it is also set as special case
+        // the item info itself would return 64
+        if (this == AIR) {
+            return 0;
+        }
+        return item.components().getOrDefault(DataComponents.MAX_STACK_SIZE, 64);
+    }
+
+    @Override
+    public short getMaxDurability() {
+        return item.components().getOrDefault(DataComponents.MAX_DAMAGE, 0).shortValue();
+    }
+
+    @Override
+    public boolean isEdible() {
+        return item.components().has(DataComponents.FOOD);
+    }
+
+    @Override
+    public boolean isRecord() {
+        return item instanceof ItemRecord;
+    }
+
+    @Override
+    public boolean isFuel() {
+        return TileEntityFurnace.isFuel(new net.minecraft.world.item.ItemStack(item));
+    }
+
+    @Override
+    public ItemType getCraftingRemainingItem() {
+        Item expectedItem = item.getCraftingRemainingItem();
+        return expectedItem == null ? null : minecraftToBukkitNew(expectedItem);
+    }
+
+//    @Override
+//    public EquipmentSlot getEquipmentSlot() {
+//        return CraftEquipmentSlot.getSlot(EntityInsentient.getEquipmentSlotForItem(CraftItemStack.asNMSCopy(ItemStack.of(this))));
+//    }
+
+    @Override
+    public Multimap<Attribute, AttributeModifier> getDefaultAttributeModifiers(EquipmentSlot slot) {
+        ImmutableMultimap.Builder<Attribute, AttributeModifier> defaultAttributes = ImmutableMultimap.builder();
+
+        ItemAttributeModifiers nmsDefaultAttributes = item.getDefaultAttributeModifiers();
+        nmsDefaultAttributes.forEach(CraftEquipmentSlot.getNMS(slot), (key, value) -> {
+            Attribute attribute = CraftAttribute.minecraftToBukkit(key.value());
+            defaultAttributes.put(attribute, CraftAttributeInstance.convert(value, slot));
+        });
+
+        return defaultAttributes.build();
+    }
+
+    @Override
+    public CreativeCategory getCreativeCategory() {
+        return CreativeCategory.BUILDING_BLOCKS;
+    }
+
+    @Override
+    public boolean isEnabledByFeature(@NotNull World world) {
+        Preconditions.checkNotNull(world, "World cannot be null");
+        return getHandle().isEnabled(((CraftWorld) world).getHandle().enabledFeatures());
+    }
+
+    @NotNull
+    @Override
+    public String getTranslationKey() {
+        return item.getDescriptionId();
+    }
+
+    @Override
+    public NamespacedKey getKey() {
+        return key;
+    }
+
+    @Override
+    public Material asMaterial() {
+        return Registry.MATERIAL.get(this.key);
+    }
 }
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java
index 4a318cb3d1..e9172ec6b0 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/legacy/CraftLegacy.java
@@ -192,7 +192,7 @@ public final class CraftLegacy {
             }
         }
 
-        if (mappedData == null && material.isBlock()) {
+        if (mappedData == null) {
             // Try exact match first
             IBlockData iblock = materialToData.get(materialData);
             if (iblock != null) {
diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/packs/CraftDataPackManager.java b/paper-server/src/main/java/org/bukkit/craftbukkit/packs/CraftDataPackManager.java
index dad32e0987..77e3f54c17 100644
--- a/paper-server/src/main/java/org/bukkit/craftbukkit/packs/CraftDataPackManager.java
+++ b/paper-server/src/main/java/org/bukkit/craftbukkit/packs/CraftDataPackManager.java
@@ -10,11 +10,13 @@ import net.minecraft.world.entity.EntityTypes;
 import org.bukkit.Material;
 import org.bukkit.NamespacedKey;
 import org.bukkit.World;
+import org.bukkit.block.BlockType;
 import org.bukkit.craftbukkit.CraftWorld;
 import org.bukkit.craftbukkit.block.CraftBlockType;
 import org.bukkit.craftbukkit.entity.CraftEntityType;
 import org.bukkit.craftbukkit.inventory.CraftItemType;
 import org.bukkit.entity.EntityType;
+import org.bukkit.inventory.ItemType;
 import org.bukkit.packs.DataPack;
 import org.bukkit.packs.DataPackManager;
 
@@ -89,6 +91,24 @@ public class CraftDataPackManager implements DataPackManager {
         return false;
     }
 
+    @Override
+    public boolean isEnabledByFeature(ItemType itemType, World world) {
+        Preconditions.checkArgument(itemType != null, "itemType cannot be null");
+        Preconditions.checkArgument(world != null, "world cannot be null");
+
+        CraftWorld craftWorld = ((CraftWorld) world);
+        return CraftItemType.bukkitToMinecraftNew(itemType.typed()).isEnabled(craftWorld.getHandle().enabledFeatures());
+    }
+
+    @Override
+    public boolean isEnabledByFeature(BlockType blockType, World world) {
+        Preconditions.checkArgument(blockType != null, "blockType cannot be null");
+        Preconditions.checkArgument(world != null, "world cannot be null");
+
+        CraftWorld craftWorld = ((CraftWorld) world);
+        return CraftBlockType.bukkitToMinecraftNew(blockType.typed()).isEnabled(craftWorld.getHandle().enabledFeatures());
+    }
+
     @Override
     public boolean isEnabledByFeature(EntityType entityType, World world) {
         Preconditions.checkArgument(entityType != null, "entityType cannot be null");
diff --git a/paper-server/src/test/java/org/bukkit/BlockDataTest.java b/paper-server/src/test/java/org/bukkit/BlockDataTest.java
index 982cf4b68a..988e916576 100644
--- a/paper-server/src/test/java/org/bukkit/BlockDataTest.java
+++ b/paper-server/src/test/java/org/bukkit/BlockDataTest.java
@@ -8,6 +8,7 @@ import net.minecraft.world.level.block.BlockCake;
 import net.minecraft.world.level.block.BlockChest;
 import net.minecraft.world.level.block.Blocks;
 import org.bukkit.block.BlockFace;
+import org.bukkit.block.BlockType;
 import org.bukkit.block.data.BlockData;
 import org.bukkit.block.data.type.Cake;
 import org.bukkit.block.data.type.Chest;
@@ -21,7 +22,7 @@ public class BlockDataTest extends AbstractTestingBase {
     public void testParsing() {
         BlockData cakeTest = CraftBlockData.fromData(Blocks.CAKE.defaultBlockState().setValue(BlockCake.BITES, 3));
 
-        BlockData materialString = CraftBlockData.newData(Material.CAKE, "[bites=3]");
+        BlockData materialString = CraftBlockData.newData(BlockType.CAKE, "[bites=3]");
         assertThat(materialString, is(cakeTest));
 
         BlockData combined = CraftBlockData.newData(null, "cake[bites=3]");
@@ -46,19 +47,14 @@ public class BlockDataTest extends AbstractTestingBase {
 
     @Test
     public void testDoubleMaterial() {
-        assertThrows(IllegalArgumentException.class, () -> CraftBlockData.newData(Material.CAKE, "minecraft:cake[bites=3]"));
+        assertThrows(IllegalArgumentException.class, () -> CraftBlockData.newData(BlockType.CAKE, "minecraft:cake[bites=3]"));
     }
 
     @Test
     public void testMistake() {
         BlockData cakeTest = CraftBlockData.fromData(Blocks.CAKE.defaultBlockState().setValue(BlockCake.BITES, 3));
 
-        assertThrows(IllegalArgumentException.class, () -> CraftBlockData.newData(Material.CAKE, cakeTest.toString()));
-    }
-
-    @Test
-    public void testItem() {
-        assertThrows(IllegalArgumentException.class, () -> CraftBlockData.newData(Material.DIAMOND_AXE, null));
+        assertThrows(IllegalArgumentException.class, () -> CraftBlockData.newData(BlockType.CAKE, cakeTest.toString()));
     }
 
     @Test
diff --git a/paper-server/src/test/java/org/bukkit/craftbukkit/block/BlockTypeTest.java b/paper-server/src/test/java/org/bukkit/craftbukkit/block/BlockTypeTest.java
new file mode 100644
index 0000000000..d67c0fb1b5
--- /dev/null
+++ b/paper-server/src/test/java/org/bukkit/craftbukkit/block/BlockTypeTest.java
@@ -0,0 +1,22 @@
+package org.bukkit.craftbukkit.block;
+
+import static org.bukkit.support.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import java.lang.reflect.Field;
+import org.bukkit.block.BlockType;
+import org.junit.jupiter.api.Test;
+
+public class BlockTypeTest {
+
+    // Ensures all BlockType constants have the correct generics
+    @Test
+    public void testBlockDataClasses() throws Exception {
+        for (Field f : BlockType.class.getDeclaredFields()) {
+            BlockType type = (BlockType) f.get(null);
+
+            Class<?> expected = type.getBlockDataClass();
+            Class<?> actual = type.typed().createBlockData().getClass().getInterfaces()[0];
+            assertThat(actual, is(expected));
+        }
+    }
+}
diff --git a/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java b/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java
index c491b9933e..675a1838c5 100644
--- a/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java
+++ b/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java
@@ -22,6 +22,7 @@ import org.bukkit.FireworkEffect.Type;
 import org.bukkit.Material;
 import org.bukkit.MusicInstrument;
 import org.bukkit.NamespacedKey;
+import org.bukkit.Registry;
 import org.bukkit.attribute.Attribute;
 import org.bukkit.attribute.AttributeModifier;
 import org.bukkit.block.banner.Pattern;
@@ -35,6 +36,7 @@ import org.bukkit.enchantments.Enchantment;
 import org.bukkit.entity.Axolotl;
 import org.bukkit.entity.TropicalFish;
 import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.ItemType;
 import org.bukkit.inventory.meta.ArmorMeta;
 import org.bukkit.inventory.meta.AxolotlBucketMeta;
 import org.bukkit.inventory.meta.BannerMeta;
@@ -452,6 +454,29 @@ public class ItemMetaTest extends AbstractTestingBase {
         assertThat(itemMeta.getBlockData(Material.CHEST), is(CraftBlockData.newData(null, "minecraft:chest[waterlogged=true]")));
     }
 
+    @Test
+    public void testMetaClasses() {
+        Registry.ITEM.forEach(itemType -> {
+            if (itemType == ItemType.AIR) {
+                return;
+            }
+
+            ItemMeta meta = new ItemStack(itemType.asMaterial()).getItemMeta();
+            Class<?> internal = meta == null ? CraftMetaItem.class : meta.getClass();
+            Class<?>[] interfaces = internal.getInterfaces();
+            Class<?> expected;
+            if (interfaces.length > 0) {
+                expected = interfaces[0];
+            } else {
+                expected = ItemMeta.class;
+            }
+
+            // Currently the expected and actual for AIR are ItemMeta rather than null
+            Class<?> actual = itemType.getItemMetaClass();
+            assertThat(actual, is(expected));
+        });
+    }
+
     private void downCastTest(final StackWrapper provider) {
         final String name = provider.toString();
         final ItemStack blank = new ItemStack(Material.STONE);
diff --git a/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/ItemTypeTest.java b/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/ItemTypeTest.java
new file mode 100644
index 0000000000..be1be8d33f
--- /dev/null
+++ b/paper-server/src/test/java/org/bukkit/craftbukkit/inventory/ItemTypeTest.java
@@ -0,0 +1,76 @@
+package org.bukkit.craftbukkit.inventory;
+
+import static org.bukkit.support.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import java.lang.reflect.Field;
+import org.bukkit.Material;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.ItemType;
+import org.bukkit.inventory.meta.ArmorMeta;
+import org.bukkit.inventory.meta.EnchantmentStorageMeta;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.inventory.meta.trim.ArmorTrim;
+import org.bukkit.inventory.meta.trim.TrimMaterial;
+import org.bukkit.inventory.meta.trim.TrimPattern;
+import org.bukkit.support.AbstractTestingBase;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class ItemTypeTest extends AbstractTestingBase {
+
+    // Ensures all ItemType constants have the correct generics
+    @Test
+    public void testItemMetaClasses() throws Exception {
+        for (Field f : ItemType.class.getDeclaredFields()) {
+            ItemType type = (ItemType) f.get(null);
+            if (type == ItemType.AIR) {
+                continue;
+            }
+
+            ItemMeta meta = new ItemStack(type.asMaterial()).getItemMeta();
+            Class<?> internal = meta == null ? CraftMetaItem.class : meta.getClass();
+            Class<?>[] interfaces = internal.getInterfaces();
+            Class<?> expected;
+            if (interfaces.length > 0) {
+                expected = interfaces[0];
+            } else {
+                expected = ItemMeta.class;
+            }
+
+            Class<?> actual = type.getItemMetaClass();
+            assertThat(actual, is(expected));
+        }
+
+        assertThrows(UnsupportedOperationException.class, () -> ItemType.AIR.getItemMetaClass());
+    }
+
+    @Test
+    public void testStaticItemTypeUsage() {
+        final ItemStack itemStack = ItemType.DIAMOND.createItemStack();
+        Assertions.assertEquals(itemStack.getType(), Material.DIAMOND);
+        Assertions.assertEquals(itemStack.getType().asItemType(), ItemType.DIAMOND);
+    }
+
+    @Test
+    public void testStaticItemTypeUsageBuilder() {
+        final ItemStack armor = ItemType.DIAMOND_LEGGINGS.createItemStack(a ->
+            a.setTrim(new ArmorTrim(TrimMaterial.EMERALD, TrimPattern.COAST))
+        );
+
+        final ItemMeta itemMeta = armor.getItemMeta();
+        Assertions.assertInstanceOf(ArmorMeta.class, itemMeta);
+
+        final ArmorTrim trim = ((ArmorMeta) itemMeta).getTrim();
+        Assertions.assertEquals(trim.getMaterial(), TrimMaterial.EMERALD);
+        Assertions.assertEquals(trim.getPattern(), TrimPattern.COAST);
+    }
+
+    @Test
+    public void testRetyping() {
+        final ItemType itemType = ItemType.ENCHANTED_BOOK;
+        Assertions.assertDoesNotThrow(() -> itemType.typed());
+        Assertions.assertDoesNotThrow(() -> itemType.typed(EnchantmentStorageMeta.class));
+        Assertions.assertThrows(IllegalArgumentException.class, () -> itemType.typed(ArmorMeta.class));
+    }
+}
diff --git a/paper-server/src/test/java/org/bukkit/registry/RegistryConversionTest.java b/paper-server/src/test/java/org/bukkit/registry/RegistryConversionTest.java
index 69dc9bbdf4..292cf337e4 100644
--- a/paper-server/src/test/java/org/bukkit/registry/RegistryConversionTest.java
+++ b/paper-server/src/test/java/org/bukkit/registry/RegistryConversionTest.java
@@ -31,6 +31,9 @@ public class RegistryConversionTest extends AbstractTestingBase {
     private static final String MINECRAFT_TO_BUKKIT = "minecraftToBukkit";
     private static final String BUKKIT_TO_MINECRAFT = "bukkitToMinecraft";
 
+    private static final String MINECRAFT_TO_BUKKIT_NEW = "minecraftToBukkitNew";
+    private static final String BUKKIT_TO_MINECRAFT_NEW = "bukkitToMinecraftNew";
+
     private static final Map<Class<? extends Keyed>, Method> MINECRAFT_TO_BUKKIT_METHODS = new HashMap<>();
     private static final Map<Class<? extends Keyed>, Method> BUKKIT_TO_MINECRAFT_METHODS = new HashMap<>();
 
@@ -61,17 +64,18 @@ public class RegistryConversionTest extends AbstractTestingBase {
     @Order(2)
     @RegistriesTest
     public void testMinecraftToBukkitPresent(Class<? extends Keyed> clazz, ResourceKey<IRegistry<?>> registryKey,
-                                             Class<? extends Keyed> craftClazz, Class<?> minecraftClazz) {
+                                             Class<? extends Keyed> craftClazz, Class<?> minecraftClazz, boolean newMethod) {
+        String methodName = (newMethod) ? MINECRAFT_TO_BUKKIT_NEW : MINECRAFT_TO_BUKKIT;
         Method method = null;
         try {
-            method = craftClazz.getDeclaredMethod(MINECRAFT_TO_BUKKIT, minecraftClazz);
+            method = craftClazz.getDeclaredMethod(methodName, minecraftClazz);
         } catch (NoSuchMethodException e) {
             fail(String.format("""
                     The class %s does not have a public static method to convert a minecraft value to a bukkit value.
 
                     Following method should be add which, returns the bukkit value based on the minecraft value.
                     %s
-                    """, craftClazz, buildMinecraftToBukkitMethod(clazz, minecraftClazz)));
+                    """, craftClazz, buildMinecraftToBukkitMethod(clazz, methodName, minecraftClazz)));
         }
 
         assertTrue(Modifier.isPublic(method.getModifiers()), String.format("""
@@ -79,47 +83,48 @@ public class RegistryConversionTest extends AbstractTestingBase {
 
                 The method should be made public, method structure:
                 %s
-                """, MINECRAFT_TO_BUKKIT, craftClazz, buildMinecraftToBukkitMethod(clazz, minecraftClazz)));
+                """, methodName, craftClazz, buildMinecraftToBukkitMethod(clazz, methodName, minecraftClazz)));
 
         assertTrue(Modifier.isStatic(method.getModifiers()), String.format("""
                 The method %s in class %s is not static.
 
                 The method should be made static, method structure:
                 %s
-                """, MINECRAFT_TO_BUKKIT, craftClazz, buildMinecraftToBukkitMethod(clazz, minecraftClazz)));
+                """, methodName, craftClazz, buildMinecraftToBukkitMethod(clazz, methodName, minecraftClazz)));
 
         assertSame(clazz, method.getReturnType(), String.format("""
                 The method %s in class %s has the wrong return value.
 
                 The method should have the correct return value, method structure:
                 %s
-                """, MINECRAFT_TO_BUKKIT, craftClazz, buildMinecraftToBukkitMethod(clazz, minecraftClazz)));
+                """, methodName, craftClazz, buildMinecraftToBukkitMethod(clazz, methodName, minecraftClazz)));
 
         MINECRAFT_TO_BUKKIT_METHODS.put(clazz, method);
     }
 
-    private String buildMinecraftToBukkitMethod(Class<? extends Keyed> clazz, Class<?> minecraftClazz) {
+    private String buildMinecraftToBukkitMethod(Class<? extends Keyed> clazz, String methodName, Class<?> minecraftClazz) {
         return String.format("""
-                public static %s minecraftToBukkit(%s minecraft) {
+                public static %s %s(%s minecraft) {
                     [...]
                 }
-                """, clazz.getSimpleName(), minecraftClazz.getName());
+                """, clazz.getSimpleName(), methodName, minecraftClazz.getName());
     }
 
     @Order(2)
     @RegistriesTest
     public void testBukkitToMinecraftPresent(Class<? extends Keyed> clazz, ResourceKey<IRegistry<?>> registryKey,
-                                             Class<? extends Keyed> craftClazz, Class<?> minecraftClazz) {
+                                             Class<? extends Keyed> craftClazz, Class<?> minecraftClazz, boolean newMethod) {
+        String methodName = (newMethod) ? BUKKIT_TO_MINECRAFT_NEW : BUKKIT_TO_MINECRAFT;
         Method method = null;
         try {
-            method = craftClazz.getDeclaredMethod(BUKKIT_TO_MINECRAFT, clazz);
+            method = craftClazz.getDeclaredMethod(methodName, clazz);
         } catch (NoSuchMethodException e) {
             fail(String.format("""
                     The class %s does not have a public static method to convert a bukkit value to a minecraft value.
 
                     Following method should be add which, returns the minecraft value based on the bukkit value.
                     %s
-                    """, craftClazz, buildBukkitToMinecraftMethod(clazz, minecraftClazz)));
+                    """, craftClazz, buildBukkitToMinecraftMethod(clazz, methodName, minecraftClazz)));
         }
 
         assertTrue(Modifier.isPublic(method.getModifiers()), String.format("""
@@ -127,31 +132,31 @@ public class RegistryConversionTest extends AbstractTestingBase {
 
                 The method should be made public, method structure:
                 %s
-                """, BUKKIT_TO_MINECRAFT, craftClazz, buildBukkitToMinecraftMethod(clazz, minecraftClazz)));
+                """, methodName, craftClazz, buildBukkitToMinecraftMethod(clazz, methodName, minecraftClazz)));
 
         assertTrue(Modifier.isStatic(method.getModifiers()), String.format("""
                 The method %s in class %s is not static.
 
                 The method should be made static, method structure:
                 %s
-                """, BUKKIT_TO_MINECRAFT, craftClazz, buildBukkitToMinecraftMethod(clazz, minecraftClazz)));
+                """, methodName, craftClazz, buildBukkitToMinecraftMethod(clazz, methodName, minecraftClazz)));
 
         assertSame(minecraftClazz, method.getReturnType(), String.format("""
                 The method %s in class %s has the wrong return value.
 
                 The method should have the correct return value, method structure:
                 %s
-                """, BUKKIT_TO_MINECRAFT, craftClazz, buildBukkitToMinecraftMethod(clazz, minecraftClazz)));
+                """, methodName, craftClazz, buildBukkitToMinecraftMethod(clazz, methodName, minecraftClazz)));
 
         BUKKIT_TO_MINECRAFT_METHODS.put(clazz, method);
     }
 
-    private String buildBukkitToMinecraftMethod(Class<? extends Keyed> clazz, Class<?> minecraftClazz) {
+    private String buildBukkitToMinecraftMethod(Class<? extends Keyed> clazz, String methodName, Class<?> minecraftClazz) {
         return String.format("""
-                public static %s bukkitToMinecraft(%s bukkit) {
+                public static %s %s(%s bukkit) {
                     [...]
                 }
-                """, minecraftClazz.getName(), clazz.getSimpleName());
+                """, minecraftClazz.getName(), methodName, clazz.getSimpleName());
     }
 
     @Order(2)
diff --git a/paper-server/src/test/java/org/bukkit/support/DummyServer.java b/paper-server/src/test/java/org/bukkit/support/DummyServer.java
index ee0cff8437..4153866f3e 100644
--- a/paper-server/src/test/java/org/bukkit/support/DummyServer.java
+++ b/paper-server/src/test/java/org/bukkit/support/DummyServer.java
@@ -40,7 +40,7 @@ public final class DummyServer {
 
             when(instance.getUnsafe()).then(mock -> CraftMagicNumbers.INSTANCE);
 
-            when(instance.createBlockData(any(Material.class))).then(mock -> CraftBlockData.newData(mock.getArgument(0), null));
+            when(instance.createBlockData(any(Material.class))).then(mock -> CraftBlockData.newData(((Material) mock.getArgument(0)).asBlockType(), null));
 
             when(instance.getLootTable(any())).then(mock -> new CraftLootTable(mock.getArgument(0),
                     AbstractTestingBase.DATA_PACK.fullRegistries().getLootTable(ResourceKey.create(Registries.LOOT_TABLE, CraftNamespacedKey.toMinecraft(mock.getArgument(0))))));
diff --git a/paper-server/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java b/paper-server/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java
index d770268353..22c2efb665 100644
--- a/paper-server/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java
+++ b/paper-server/src/test/java/org/bukkit/support/provider/RegistriesArgumentProvider.java
@@ -4,18 +4,22 @@ import com.google.common.collect.Lists;
 import java.util.List;
 import java.util.stream.Stream;
 import net.minecraft.core.registries.Registries;
+import net.minecraft.resources.ResourceKey;
 import net.minecraft.world.effect.MobEffectList;
 import net.minecraft.world.entity.animal.WolfVariant;
 import net.minecraft.world.item.Instrument;
 import org.bukkit.GameEvent;
 import org.bukkit.MusicInstrument;
+import org.bukkit.block.BlockType;
 import org.bukkit.craftbukkit.CraftGameEvent;
 import org.bukkit.craftbukkit.CraftMusicInstrument;
+import org.bukkit.craftbukkit.block.CraftBlockType;
 import org.bukkit.craftbukkit.damage.CraftDamageType;
 import org.bukkit.craftbukkit.enchantments.CraftEnchantment;
 import org.bukkit.craftbukkit.entity.CraftWolf;
 import org.bukkit.craftbukkit.generator.structure.CraftStructure;
 import org.bukkit.craftbukkit.generator.structure.CraftStructureType;
+import org.bukkit.craftbukkit.inventory.CraftItemType;
 import org.bukkit.craftbukkit.inventory.trim.CraftTrimMaterial;
 import org.bukkit.craftbukkit.inventory.trim.CraftTrimPattern;
 import org.bukkit.craftbukkit.potion.CraftPotionEffectType;
@@ -24,6 +28,7 @@ import org.bukkit.enchantments.Enchantment;
 import org.bukkit.entity.Wolf;
 import org.bukkit.generator.structure.Structure;
 import org.bukkit.generator.structure.StructureType;
+import org.bukkit.inventory.ItemType;
 import org.bukkit.inventory.meta.trim.TrimMaterial;
 import org.bukkit.inventory.meta.trim.TrimPattern;
 import org.bukkit.potion.PotionEffectType;
@@ -37,16 +42,27 @@ public class RegistriesArgumentProvider implements ArgumentsProvider {
 
     static {
         // Order: Bukkit class, Minecraft Registry key, CraftBukkit class, Minecraft class
-        DATA.add(Arguments.of(Enchantment.class, Registries.ENCHANTMENT, CraftEnchantment.class, net.minecraft.world.item.enchantment.Enchantment.class));
-        DATA.add(Arguments.of(GameEvent.class, Registries.GAME_EVENT, CraftGameEvent.class, net.minecraft.world.level.gameevent.GameEvent.class));
-        DATA.add(Arguments.of(MusicInstrument.class, Registries.INSTRUMENT, CraftMusicInstrument.class, Instrument.class));
-        DATA.add(Arguments.of(PotionEffectType.class, Registries.MOB_EFFECT, CraftPotionEffectType.class, MobEffectList.class));
-        DATA.add(Arguments.of(Structure.class, Registries.STRUCTURE, CraftStructure.class, net.minecraft.world.level.levelgen.structure.Structure.class));
-        DATA.add(Arguments.of(StructureType.class, Registries.STRUCTURE_TYPE, CraftStructureType.class, net.minecraft.world.level.levelgen.structure.StructureType.class));
-        DATA.add(Arguments.of(TrimMaterial.class, Registries.TRIM_MATERIAL, CraftTrimMaterial.class, net.minecraft.world.item.armortrim.TrimMaterial.class));
-        DATA.add(Arguments.of(TrimPattern.class, Registries.TRIM_PATTERN, CraftTrimPattern.class, net.minecraft.world.item.armortrim.TrimPattern.class));
-        DATA.add(Arguments.of(DamageType.class, Registries.DAMAGE_TYPE, CraftDamageType.class, net.minecraft.world.damagesource.DamageType.class));
-        DATA.add(Arguments.of(Wolf.Variant.class, Registries.WOLF_VARIANT, CraftWolf.CraftVariant.class, WolfVariant.class));
+        register(Enchantment.class, Registries.ENCHANTMENT, CraftEnchantment.class, net.minecraft.world.item.enchantment.Enchantment.class);
+        register(GameEvent.class, Registries.GAME_EVENT, CraftGameEvent.class, net.minecraft.world.level.gameevent.GameEvent.class);
+        register(MusicInstrument.class, Registries.INSTRUMENT, CraftMusicInstrument.class, Instrument.class);
+        register(PotionEffectType.class, Registries.MOB_EFFECT, CraftPotionEffectType.class, MobEffectList.class);
+        register(Structure.class, Registries.STRUCTURE, CraftStructure.class, net.minecraft.world.level.levelgen.structure.Structure.class);
+        register(StructureType.class, Registries.STRUCTURE_TYPE, CraftStructureType.class, net.minecraft.world.level.levelgen.structure.StructureType.class);
+        register(TrimMaterial.class, Registries.TRIM_MATERIAL, CraftTrimMaterial.class, net.minecraft.world.item.armortrim.TrimMaterial.class);
+        register(TrimPattern.class, Registries.TRIM_PATTERN, CraftTrimPattern.class, net.minecraft.world.item.armortrim.TrimPattern.class);
+        register(DamageType.class, Registries.DAMAGE_TYPE, CraftDamageType.class, net.minecraft.world.damagesource.DamageType.class);
+        register(Wolf.Variant.class, Registries.WOLF_VARIANT, CraftWolf.CraftVariant.class, WolfVariant.class);
+        register(ItemType.class, Registries.ITEM, CraftItemType.class, net.minecraft.world.item.Item.class, true);
+        register(BlockType.class, Registries.BLOCK, CraftBlockType.class, net.minecraft.world.level.block.Block.class, true);
+
+    }
+
+    private static void register(Class bukkit, ResourceKey registry, Class craft, Class minecraft) {
+        register(bukkit, registry, craft, minecraft, false);
+    }
+
+    private static void register(Class bukkit, ResourceKey registry, Class craft, Class minecraft, boolean newClass) {
+        DATA.add(Arguments.of(bukkit, registry, craft, minecraft, newClass));
     }
 
     @Override