aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0637-Fix-upstreams-block-state-factories.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/server/0637-Fix-upstreams-block-state-factories.patch')
-rw-r--r--patches/server/0637-Fix-upstreams-block-state-factories.patch485
1 files changed, 485 insertions, 0 deletions
diff --git a/patches/server/0637-Fix-upstreams-block-state-factories.patch b/patches/server/0637-Fix-upstreams-block-state-factories.patch
new file mode 100644
index 0000000000..2fdb11ce82
--- /dev/null
+++ b/patches/server/0637-Fix-upstreams-block-state-factories.patch
@@ -0,0 +1,485 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jake Potrebic <[email protected]>
+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 6e24673a793017ee857cf75bf9a74105ce76b773..e3c5f99b3ad91a9bb454f9ab95b1ccff0bb7b34c 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
+@@ -371,7 +371,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 92133f16c192f5caf9962a08401ff914550747f8..397eb1a101bd60f49dbb2fa8eddf28f6f233167f 100644
+--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
++++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
+@@ -20,7 +20,7 @@ import org.bukkit.persistence.PersistentDataContainer;
+ import org.jetbrains.annotations.NotNull;
+ import org.jetbrains.annotations.Nullable;
+
+-public class CraftBlockEntityState<T extends BlockEntity> extends CraftBlockState implements TileState {
++public abstract class CraftBlockEntityState<T extends BlockEntity> extends CraftBlockState implements TileState { // Paper - revert upstream's revert of the block state changes
+
+ private final T tileEntity;
+ private final T snapshot;
+@@ -181,14 +181,10 @@ public class CraftBlockEntityState<T extends BlockEntity> extends CraftBlockStat
+ }
+
+ @Override
+- public CraftBlockEntityState<T> copy() {
+- return new CraftBlockEntityState<>(this, null);
+- }
++ public abstract CraftBlockEntityState<T> copy(); // Paper - make abstract
+
+ @Override
+- public CraftBlockEntityState<T> copy(Location location) {
+- return new CraftBlockEntityState<>(this, location);
+- }
++ public abstract CraftBlockEntityState<T> 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 83ac5fc6cbbd249b5865ab203b150f53f01c9f05..b7ff7af2513204b151340538d50a65c850bdb75f 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;
+@@ -87,9 +88,9 @@ public final class CraftBlockStates {
+ private static class BlockEntityStateFactory<T extends BlockEntity, B extends CraftBlockEntityState<T>> extends BlockStateFactory<B> {
+
+ private final BiFunction<World, T, B> blockStateConstructor;
+- private final BiFunction<BlockPos, net.minecraft.world.level.block.state.BlockState, T> tileEntityConstructor;
++ private final BlockEntityType<? extends T> tileEntityConstructor; // Paper
+
+- protected BlockEntityStateFactory(Class<B> blockStateType, BiFunction<World, T, B> blockStateConstructor, BiFunction<BlockPos, net.minecraft.world.level.block.state.BlockState, T> tileEntityConstructor) {
++ protected BlockEntityStateFactory(Class<B> blockStateType, BiFunction<World, T, B> blockStateConstructor, BlockEntityType<? extends T> tileEntityConstructor) { // Paper
+ super(blockStateType);
+ this.blockStateConstructor = blockStateConstructor;
+ this.tileEntityConstructor = tileEntityConstructor;
+@@ -106,7 +107,7 @@ public final class CraftBlockStates {
+ }
+
+ private T createTileEntity(BlockPos blockPosition, net.minecraft.world.level.block.state.BlockState blockData) {
+- return this.tileEntityConstructor.apply(blockPosition, blockData);
++ return this.tileEntityConstructor.create(blockPosition, blockData); // Paper
+ }
+
+ private B createBlockState(World world, T tileEntity) {
+@@ -118,228 +119,65 @@ public final class CraftBlockStates {
+ private static final BlockStateFactory<?> DEFAULT_FACTORY = new BlockStateFactory<CraftBlockState>(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<BlockEntityType<?>, 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 <T extends BlockEntity, B extends CraftBlockEntityState<T>> void register(
+- Material blockType,
++ net.minecraft.world.level.block.entity.BlockEntityType<? extends T> blockEntityType, // Paper
+ Class<B> blockStateType,
+- BiFunction<World, T, B> blockStateConstructor,
+- BiFunction<BlockPos, net.minecraft.world.level.block.state.BlockState, T> tileEntityConstructor
++ BiFunction<World, T, B> blockStateConstructor // Paper
+ ) {
+- CraftBlockStates.register(Collections.singletonList(blockType), blockStateType, blockStateConstructor, tileEntityConstructor);
+- }
+-
+- private static <T extends BlockEntity, B extends CraftBlockEntityState<T>> void register(
+- List<Material> blockTypes,
+- Class<B> blockStateType,
+- BiFunction<World, T, B> blockStateConstructor,
+- BiFunction<BlockPos, net.minecraft.world.level.block.state.BlockState, T> tileEntityConstructor
+- ) {
+- BlockStateFactory<B> factory = new BlockEntityStateFactory<>(blockStateType, blockStateConstructor, tileEntityConstructor);
+- for (Material blockType : blockTypes) {
+- CraftBlockStates.register(blockType, factory);
++ // Paper start
++ BlockStateFactory<B> factory = new BlockEntityStateFactory<>(blockStateType, blockStateConstructor, blockEntityType); // Paper
++ 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<? extends CraftBlockState> 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<? extends CraftBlockState> 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);
+ }
+@@ -472,6 +320,14 @@ public final class CraftBlockStates {
+ return new CraftBlockState(CraftBlock.at(world, pos), flag);
+ }
+
++ // Paper start
++ @Nullable
++ public static BlockEntityType<?> getBlockEntityType(final Material material) {
++ final BlockStateFactory<?> factory = org.bukkit.craftbukkit.block.CraftBlockStates.FACTORIES.get(material);
++ return factory instanceof final BlockEntityStateFactory<?,?> blockEntityStateFactory ? blockEntityStateFactory.tileEntityConstructor : null;
++ }
++ // Paper end
++
+ private CraftBlockStates() {
+ }
+ }
+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
+ }