From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Wed, 6 Oct 2021 20:50:48 -0700 Subject: [PATCH] Fix upstreams block state factories Sometimes, blocks are changed and then logic is called before the associated tile entity is removed. When this happens, the factories were relying on the block at the position, not the tile entity. This change prioritizes using the tile entity type to determine the block state factory and falls back on the material type of the block at that location. == AT == public net.minecraft.world.level.block.entity.BlockEntityType validBlocks diff --git a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java index d22cc83725cee3df20bb6f99de23aceb62923eef..978125c69b2a9ea465595c7eef10a1aea7ccf26c 100644 --- a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java @@ -361,7 +361,7 @@ public abstract class BlockEntity { // Paper end if (this.level == null) return null; org.bukkit.block.Block block = this.level.getWorld().getBlockAt(this.worldPosition.getX(), this.worldPosition.getY(), this.worldPosition.getZ()); - if (block.getType() == org.bukkit.Material.AIR) return null; + // if (block.getType() == org.bukkit.Material.AIR) return null; // Paper - actually get the tile entity if it still exists org.bukkit.block.BlockState state = block.getState(useSnapshot); // Paper if (state instanceof InventoryHolder) return (InventoryHolder) state; return null; diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java index 89bd200befb65fe33ee049f7a07d5c771c971b13..f97d47677e13441c0b39eb8d18ebee428ea53ca4 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java @@ -18,7 +18,7 @@ import org.bukkit.persistence.PersistentDataContainer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class CraftBlockEntityState extends CraftBlockState implements TileState { +public abstract class CraftBlockEntityState extends CraftBlockState implements TileState { // Paper - revert upstream's revert of the block state changes private final T tileEntity; private final T snapshot; @@ -178,14 +178,10 @@ public class CraftBlockEntityState extends CraftBlockStat } @Override - public CraftBlockEntityState copy() { - return new CraftBlockEntityState<>(this, null); - } + public abstract CraftBlockEntityState copy(); // Paper - make abstract @Override - public CraftBlockEntityState copy(Location location) { - return new CraftBlockEntityState<>(this, location); - } + public abstract CraftBlockEntityState copy(Location location); // Paper - make abstract // Paper start @Override diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java index e40973b65ece11d9c5a76abad51f72e610bf02ab..411c2de93c71e480f95229c882cdf43b8801edc8 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java @@ -22,6 +22,7 @@ import net.minecraft.world.level.block.entity.BeehiveBlockEntity; import net.minecraft.world.level.block.entity.BellBlockEntity; import net.minecraft.world.level.block.entity.BlastFurnaceBlockEntity; import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; // Paper import net.minecraft.world.level.block.entity.BrewingStandBlockEntity; import net.minecraft.world.level.block.entity.BrushableBlockEntity; import net.minecraft.world.level.block.entity.CalibratedSculkSensorBlockEntity; @@ -118,228 +119,65 @@ public final class CraftBlockStates { private static final BlockStateFactory DEFAULT_FACTORY = new BlockStateFactory(CraftBlockState.class) { @Override public CraftBlockState createBlockState(World world, BlockPos blockPosition, net.minecraft.world.level.block.state.BlockState blockData, BlockEntity tileEntity) { - // SPIGOT-6754, SPIGOT-6817: Restore previous behaviour for tile entities with removed blocks (loot generation post-destroy) - if (tileEntity != null) { - // block with unhandled TileEntity: - return new CraftBlockEntityState<>(world, tileEntity); - } + // Paper - revert upstream's revert of the block state changes. Block entities that have already had the block type set to AIR are still valid, upstream decided to ignore them Preconditions.checkState(tileEntity == null, "Unexpected BlockState for %s", CraftBlockType.minecraftToBukkit(blockData.getBlock())); return new CraftBlockState(world, blockPosition, blockData); } }; + // Paper start + private static final Map, BlockStateFactory> FACTORIES_BY_BLOCK_ENTITY_TYPE = new HashMap<>(); + private static void register(BlockEntityType type, BlockStateFactory factory) { + FACTORIES_BY_BLOCK_ENTITY_TYPE.put(type, factory); + } + // Paper end static { - register( - Arrays.asList( - Material.ACACIA_SIGN, - Material.ACACIA_WALL_SIGN, - Material.BAMBOO_SIGN, - Material.BAMBOO_WALL_SIGN, - Material.BIRCH_SIGN, - Material.BIRCH_WALL_SIGN, - Material.CHERRY_SIGN, - Material.CHERRY_WALL_SIGN, - Material.CRIMSON_SIGN, - Material.CRIMSON_WALL_SIGN, - Material.DARK_OAK_SIGN, - Material.DARK_OAK_WALL_SIGN, - Material.JUNGLE_SIGN, - Material.JUNGLE_WALL_SIGN, - Material.MANGROVE_SIGN, - Material.MANGROVE_WALL_SIGN, - Material.OAK_SIGN, - Material.OAK_WALL_SIGN, - Material.SPRUCE_SIGN, - Material.SPRUCE_WALL_SIGN, - Material.WARPED_SIGN, - Material.WARPED_WALL_SIGN - ), CraftSign.class, CraftSign::new, SignBlockEntity::new - ); - - register( - Arrays.asList( - Material.ACACIA_HANGING_SIGN, - Material.ACACIA_WALL_HANGING_SIGN, - Material.BAMBOO_HANGING_SIGN, - Material.BAMBOO_WALL_HANGING_SIGN, - Material.BIRCH_HANGING_SIGN, - Material.BIRCH_WALL_HANGING_SIGN, - Material.CHERRY_HANGING_SIGN, - Material.CHERRY_WALL_HANGING_SIGN, - Material.CRIMSON_HANGING_SIGN, - Material.CRIMSON_WALL_HANGING_SIGN, - Material.DARK_OAK_HANGING_SIGN, - Material.DARK_OAK_WALL_HANGING_SIGN, - Material.JUNGLE_HANGING_SIGN, - Material.JUNGLE_WALL_HANGING_SIGN, - Material.MANGROVE_HANGING_SIGN, - Material.MANGROVE_WALL_HANGING_SIGN, - Material.OAK_HANGING_SIGN, - Material.OAK_WALL_HANGING_SIGN, - Material.SPRUCE_HANGING_SIGN, - Material.SPRUCE_WALL_HANGING_SIGN, - Material.WARPED_HANGING_SIGN, - Material.WARPED_WALL_HANGING_SIGN - ), CraftHangingSign.class, CraftHangingSign::new, HangingSignBlockEntity::new - ); - - register( - Arrays.asList( - Material.CREEPER_HEAD, - Material.CREEPER_WALL_HEAD, - Material.DRAGON_HEAD, - Material.DRAGON_WALL_HEAD, - Material.PIGLIN_HEAD, - Material.PIGLIN_WALL_HEAD, - Material.PLAYER_HEAD, - Material.PLAYER_WALL_HEAD, - Material.SKELETON_SKULL, - Material.SKELETON_WALL_SKULL, - Material.WITHER_SKELETON_SKULL, - Material.WITHER_SKELETON_WALL_SKULL, - Material.ZOMBIE_HEAD, - Material.ZOMBIE_WALL_HEAD - ), CraftSkull.class, CraftSkull::new, SkullBlockEntity::new - ); - - register( - Arrays.asList( - Material.COMMAND_BLOCK, - Material.REPEATING_COMMAND_BLOCK, - Material.CHAIN_COMMAND_BLOCK - ), CraftCommandBlock.class, CraftCommandBlock::new, CommandBlockEntity::new - ); - - register( - Arrays.asList( - Material.BLACK_BANNER, - Material.BLACK_WALL_BANNER, - Material.BLUE_BANNER, - Material.BLUE_WALL_BANNER, - Material.BROWN_BANNER, - Material.BROWN_WALL_BANNER, - Material.CYAN_BANNER, - Material.CYAN_WALL_BANNER, - Material.GRAY_BANNER, - Material.GRAY_WALL_BANNER, - Material.GREEN_BANNER, - Material.GREEN_WALL_BANNER, - Material.LIGHT_BLUE_BANNER, - Material.LIGHT_BLUE_WALL_BANNER, - Material.LIGHT_GRAY_BANNER, - Material.LIGHT_GRAY_WALL_BANNER, - Material.LIME_BANNER, - Material.LIME_WALL_BANNER, - Material.MAGENTA_BANNER, - Material.MAGENTA_WALL_BANNER, - Material.ORANGE_BANNER, - Material.ORANGE_WALL_BANNER, - Material.PINK_BANNER, - Material.PINK_WALL_BANNER, - Material.PURPLE_BANNER, - Material.PURPLE_WALL_BANNER, - Material.RED_BANNER, - Material.RED_WALL_BANNER, - Material.WHITE_BANNER, - Material.WHITE_WALL_BANNER, - Material.YELLOW_BANNER, - Material.YELLOW_WALL_BANNER - ), CraftBanner.class, CraftBanner::new, BannerBlockEntity::new - ); - - register( - Arrays.asList( - Material.SHULKER_BOX, - Material.WHITE_SHULKER_BOX, - Material.ORANGE_SHULKER_BOX, - Material.MAGENTA_SHULKER_BOX, - Material.LIGHT_BLUE_SHULKER_BOX, - Material.YELLOW_SHULKER_BOX, - Material.LIME_SHULKER_BOX, - Material.PINK_SHULKER_BOX, - Material.GRAY_SHULKER_BOX, - Material.LIGHT_GRAY_SHULKER_BOX, - Material.CYAN_SHULKER_BOX, - Material.PURPLE_SHULKER_BOX, - Material.BLUE_SHULKER_BOX, - Material.BROWN_SHULKER_BOX, - Material.GREEN_SHULKER_BOX, - Material.RED_SHULKER_BOX, - Material.BLACK_SHULKER_BOX - ), CraftShulkerBox.class, CraftShulkerBox::new, ShulkerBoxBlockEntity::new - ); - - register( - Arrays.asList( - Material.BLACK_BED, - Material.BLUE_BED, - Material.BROWN_BED, - Material.CYAN_BED, - Material.GRAY_BED, - Material.GREEN_BED, - Material.LIGHT_BLUE_BED, - Material.LIGHT_GRAY_BED, - Material.LIME_BED, - Material.MAGENTA_BED, - Material.ORANGE_BED, - Material.PINK_BED, - Material.PURPLE_BED, - Material.RED_BED, - Material.WHITE_BED, - Material.YELLOW_BED - ), CraftBed.class, CraftBed::new, BedBlockEntity::new - ); - - register( - Arrays.asList( - Material.BEEHIVE, - Material.BEE_NEST - ), CraftBeehive.class, CraftBeehive::new, BeehiveBlockEntity::new - ); - - register( - Arrays.asList( - Material.CAMPFIRE, - Material.SOUL_CAMPFIRE - ), CraftCampfire.class, CraftCampfire::new, CampfireBlockEntity::new - ); - - register(Material.BARREL, CraftBarrel.class, CraftBarrel::new, BarrelBlockEntity::new); - register(Material.BEACON, CraftBeacon.class, CraftBeacon::new, BeaconBlockEntity::new); - register(Material.BELL, CraftBell.class, CraftBell::new, BellBlockEntity::new); - register(Material.BLAST_FURNACE, CraftBlastFurnace.class, CraftBlastFurnace::new, BlastFurnaceBlockEntity::new); - register(Material.BREWING_STAND, CraftBrewingStand.class, CraftBrewingStand::new, BrewingStandBlockEntity::new); - register(Material.CHEST, CraftChest.class, CraftChest::new, ChestBlockEntity::new); - register(Material.CHISELED_BOOKSHELF, CraftChiseledBookshelf.class, CraftChiseledBookshelf::new, ChiseledBookShelfBlockEntity::new); - register(Material.COMPARATOR, CraftComparator.class, CraftComparator::new, ComparatorBlockEntity::new); - register(Material.CONDUIT, CraftConduit.class, CraftConduit::new, ConduitBlockEntity::new); - register(Material.DAYLIGHT_DETECTOR, CraftDaylightDetector.class, CraftDaylightDetector::new, DaylightDetectorBlockEntity::new); - register(Material.DECORATED_POT, CraftDecoratedPot.class, CraftDecoratedPot::new, DecoratedPotBlockEntity::new); - register(Material.DISPENSER, CraftDispenser.class, CraftDispenser::new, DispenserBlockEntity::new); - register(Material.DROPPER, CraftDropper.class, CraftDropper::new, DropperBlockEntity::new); - register(Material.ENCHANTING_TABLE, CraftEnchantingTable.class, CraftEnchantingTable::new, EnchantingTableBlockEntity::new); - register(Material.ENDER_CHEST, CraftEnderChest.class, CraftEnderChest::new, EnderChestBlockEntity::new); - register(Material.END_GATEWAY, CraftEndGateway.class, CraftEndGateway::new, TheEndGatewayBlockEntity::new); - register(Material.END_PORTAL, CraftEndPortal.class, CraftEndPortal::new, TheEndPortalBlockEntity::new); - register(Material.FURNACE, CraftFurnaceFurnace.class, CraftFurnaceFurnace::new, FurnaceBlockEntity::new); - register(Material.HOPPER, CraftHopper.class, CraftHopper::new, HopperBlockEntity::new); - register(Material.JIGSAW, CraftJigsaw.class, CraftJigsaw::new, JigsawBlockEntity::new); - register(Material.JUKEBOX, CraftJukebox.class, CraftJukebox::new, JukeboxBlockEntity::new); - register(Material.LECTERN, CraftLectern.class, CraftLectern::new, LecternBlockEntity::new); - register(Material.MOVING_PISTON, CraftMovingPiston.class, CraftMovingPiston::new, PistonMovingBlockEntity::new); - register(Material.SCULK_CATALYST, CraftSculkCatalyst.class, CraftSculkCatalyst::new, SculkCatalystBlockEntity::new); - register(Material.CALIBRATED_SCULK_SENSOR, CraftCalibratedSculkSensor.class, CraftCalibratedSculkSensor::new, CalibratedSculkSensorBlockEntity::new); - register(Material.SCULK_SENSOR, CraftSculkSensor.class, CraftSculkSensor::new, SculkSensorBlockEntity::new); - register(Material.SCULK_SHRIEKER, CraftSculkShrieker.class, CraftSculkShrieker::new, SculkShriekerBlockEntity::new); - register(Material.SMOKER, CraftSmoker.class, CraftSmoker::new, SmokerBlockEntity::new); - register(Material.SPAWNER, CraftCreatureSpawner.class, CraftCreatureSpawner::new, SpawnerBlockEntity::new); - register(Material.STRUCTURE_BLOCK, CraftStructureBlock.class, CraftStructureBlock::new, StructureBlockEntity::new); - register(Material.SUSPICIOUS_SAND, CraftSuspiciousSand.class, CraftSuspiciousSand::new, BrushableBlockEntity::new); - register(Material.SUSPICIOUS_GRAVEL, CraftBrushableBlock.class, CraftBrushableBlock::new, BrushableBlockEntity::new); - register(Material.TRAPPED_CHEST, CraftChest.class, CraftChest::new, TrappedChestBlockEntity::new); - register(Material.CRAFTER, CraftCrafter.class, CraftCrafter::new, CrafterBlockEntity::new); - register(Material.TRIAL_SPAWNER, CraftTrialSpawner.class, CraftTrialSpawner::new, TrialSpawnerBlockEntity::new); - register(Material.VAULT, CraftVault.class, CraftVault::new, VaultBlockEntity::new); + // Paper start - simplify + register(BlockEntityType.SIGN, CraftSign.class, CraftSign::new); + register(BlockEntityType.HANGING_SIGN, CraftHangingSign.class, CraftHangingSign::new); + register(BlockEntityType.SKULL, CraftSkull.class, CraftSkull::new); + register(BlockEntityType.COMMAND_BLOCK, CraftCommandBlock.class, CraftCommandBlock::new); + register(BlockEntityType.BANNER, CraftBanner.class, CraftBanner::new); + register(BlockEntityType.SHULKER_BOX, CraftShulkerBox.class, CraftShulkerBox::new); + register(BlockEntityType.BED, CraftBed.class, CraftBed::new); + register(BlockEntityType.BEEHIVE, CraftBeehive.class, CraftBeehive::new); + register(BlockEntityType.CAMPFIRE, CraftCampfire.class, CraftCampfire::new); + register(BlockEntityType.BARREL, CraftBarrel.class, CraftBarrel::new); + register(BlockEntityType.BEACON, CraftBeacon.class, CraftBeacon::new); + register(BlockEntityType.BELL, CraftBell.class, CraftBell::new); + register(BlockEntityType.BLAST_FURNACE, CraftBlastFurnace.class, CraftBlastFurnace::new); + register(BlockEntityType.BREWING_STAND, CraftBrewingStand.class, CraftBrewingStand::new); + register(BlockEntityType.CHEST, CraftChest.class, CraftChest::new); + register(BlockEntityType.CHISELED_BOOKSHELF, CraftChiseledBookshelf.class, CraftChiseledBookshelf::new); + register(BlockEntityType.COMPARATOR, CraftComparator.class, CraftComparator::new); + register(BlockEntityType.CONDUIT, CraftConduit.class, CraftConduit::new); + register(BlockEntityType.DAYLIGHT_DETECTOR, CraftDaylightDetector.class, CraftDaylightDetector::new); + register(BlockEntityType.DECORATED_POT, CraftDecoratedPot.class, CraftDecoratedPot::new); + register(BlockEntityType.DISPENSER, CraftDispenser.class, CraftDispenser::new); + register(BlockEntityType.DROPPER, CraftDropper.class, CraftDropper::new); + register(BlockEntityType.ENCHANTING_TABLE, CraftEnchantingTable.class, CraftEnchantingTable::new); + register(BlockEntityType.ENDER_CHEST, CraftEnderChest.class, CraftEnderChest::new); + register(BlockEntityType.END_GATEWAY, CraftEndGateway.class, CraftEndGateway::new); + register(BlockEntityType.END_PORTAL, CraftEndPortal.class, CraftEndPortal::new); + register(BlockEntityType.FURNACE, CraftFurnaceFurnace.class, CraftFurnaceFurnace::new); + register(BlockEntityType.HOPPER, CraftHopper.class, CraftHopper::new); + register(BlockEntityType.JIGSAW, CraftJigsaw.class, CraftJigsaw::new); + register(BlockEntityType.JUKEBOX, CraftJukebox.class, CraftJukebox::new); + register(BlockEntityType.LECTERN, CraftLectern.class, CraftLectern::new); + register(BlockEntityType.PISTON, CraftMovingPiston.class, CraftMovingPiston::new); + register(BlockEntityType.SCULK_CATALYST, CraftSculkCatalyst.class, CraftSculkCatalyst::new); + register(BlockEntityType.SCULK_SENSOR, CraftSculkSensor.class, CraftSculkSensor::new); + register(BlockEntityType.SCULK_SHRIEKER, CraftSculkShrieker.class, CraftSculkShrieker::new); + register(BlockEntityType.CALIBRATED_SCULK_SENSOR, CraftCalibratedSculkSensor.class, CraftCalibratedSculkSensor::new); + register(BlockEntityType.SMOKER, CraftSmoker.class, CraftSmoker::new); + register(BlockEntityType.MOB_SPAWNER, CraftCreatureSpawner.class, CraftCreatureSpawner::new); + register(BlockEntityType.STRUCTURE_BLOCK, CraftStructureBlock.class, CraftStructureBlock::new); + register(BlockEntityType.BRUSHABLE_BLOCK, CraftBrushableBlock.class, CraftBrushableBlock::new); // note: spigot still uses CraftSuspiciousSand impl for that block type + register(BlockEntityType.TRAPPED_CHEST, CraftChest.class, CraftChest::new); + register(BlockEntityType.CRAFTER, CraftCrafter.class, CraftCrafter::new); + register(BlockEntityType.TRIAL_SPAWNER, CraftTrialSpawner.class, CraftTrialSpawner::new); + register(BlockEntityType.VAULT, CraftVault.class, CraftVault::new); + // Paper end } private static void register(Material blockType, BlockStateFactory factory) { @@ -347,30 +185,33 @@ public final class CraftBlockStates { } private static > void register( - Material blockType, - Class blockStateType, - BiFunction blockStateConstructor, - BiFunction tileEntityConstructor - ) { - CraftBlockStates.register(Collections.singletonList(blockType), blockStateType, blockStateConstructor, tileEntityConstructor); - } - - private static > void register( - List blockTypes, + net.minecraft.world.level.block.entity.BlockEntityType blockEntityType, // Paper Class blockStateType, - BiFunction blockStateConstructor, - BiFunction tileEntityConstructor + BiFunction blockStateConstructor // Paper ) { - BlockStateFactory factory = new BlockEntityStateFactory<>(blockStateType, blockStateConstructor, tileEntityConstructor); - for (Material blockType : blockTypes) { - CraftBlockStates.register(blockType, factory); + // Paper start + BlockStateFactory factory = new BlockEntityStateFactory<>(blockStateType, blockStateConstructor, blockEntityType::create); + for (net.minecraft.world.level.block.Block block : blockEntityType.validBlocks) { + CraftBlockStates.register(CraftBlockType.minecraftToBukkit(block), factory); } + CraftBlockStates.register(blockEntityType, factory); + // Paper end } private static BlockStateFactory getFactory(Material material) { return CraftBlockStates.FACTORIES.getOrDefault(material, CraftBlockStates.DEFAULT_FACTORY); } + // Paper start + private static BlockStateFactory getFactory(Material material, BlockEntityType type) { + if (type != null) { + return CraftBlockStates.FACTORIES_BY_BLOCK_ENTITY_TYPE.getOrDefault(type, getFactory(material)); + } else { + return getFactory(material); + } + } + // Paper end + public static Class getBlockStateType(Material material) { Preconditions.checkNotNull(material, "material is null"); return CraftBlockStates.getFactory(material).blockStateType; @@ -386,6 +227,13 @@ public final class CraftBlockStates { return null; } + // Paper start + public static Class getBlockStateType(BlockEntityType blockEntityType) { + Preconditions.checkNotNull(blockEntityType, "blockEntityType is null"); + return CraftBlockStates.getFactory(null, blockEntityType).blockStateType; + } + // Paper end + public static BlockState getBlockState(Block block) { // Paper start return CraftBlockStates.getBlockState(block, true); @@ -453,7 +301,7 @@ public final class CraftBlockStates { if (world != null && tileEntity == null && CraftBlockStates.isTileEntityOptional(material)) { factory = CraftBlockStates.DEFAULT_FACTORY; } else { - factory = CraftBlockStates.getFactory(material); + factory = CraftBlockStates.getFactory(material, tileEntity != null ? tileEntity.getType() : null); // Paper } return factory.createBlockState(world, blockPosition, blockData, tileEntity); } diff --git a/src/test/java/org/bukkit/craftbukkit/block/BlockStateTest.java b/src/test/java/org/bukkit/craftbukkit/block/BlockStateTest.java index 81d4c8867ebcba1b805be1828e0a6a476963a855..9ff1a8068533ba5fc2fb43188d9a5c544a907618 100644 --- a/src/test/java/org/bukkit/craftbukkit/block/BlockStateTest.java +++ b/src/test/java/org/bukkit/craftbukkit/block/BlockStateTest.java @@ -7,6 +7,7 @@ import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; import org.bukkit.Material; import org.bukkit.support.AbstractTestingBase; import org.junit.jupiter.api.Test; @@ -41,4 +42,13 @@ public class BlockStateTest extends AbstractTestingBase { } } } + + // Paper start + @Test + public void testBlockEntityTypes() { + for (var blockEntityType : BuiltInRegistries.BLOCK_ENTITY_TYPE) { + org.junit.jupiter.api.Assertions.assertNotNull(CraftBlockStates.getBlockStateType(blockEntityType)); + } + } + // Paper end }