diff --git a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java index 81822da6..fe470a70 100644 --- a/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java +++ b/api/src/main/java/com/lishid/openinv/internal/IAnySilentContainer.java @@ -16,25 +16,17 @@ package com.lishid.openinv.internal; -import org.bukkit.block.Barrel; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; import org.bukkit.block.EnderChest; -import org.bukkit.block.ShulkerBox; -import org.bukkit.block.data.BlockData; import org.bukkit.block.data.Directional; -import org.bukkit.block.data.type.Chest; import org.bukkit.entity.Cat; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryHolder; import org.bukkit.util.BoundingBox; import org.jetbrains.annotations.NotNull; -import java.lang.reflect.Method; - -import static com.lishid.openinv.util.InventoryAccess.getBlockState; - public interface IAnySilentContainer { /** @@ -61,62 +53,7 @@ public interface IAnySilentContainer { * @param block the {@link Block} of the container * @return true if the container is blocked */ - default boolean isAnyContainerNeeded(@NotNull Block block) { - BlockState blockState = getBlockState(block); - - // Barrels do not require AnyContainer. - if (blockState instanceof Barrel) { - return false; - } - - // Enderchests require a non-occluding block on top to open. - if (blockState instanceof EnderChest) { - return block.getRelative(0, 1, 0).getType().isOccluding(); - } - - // Shulker boxes require half a block clear in the direction they open. - if (blockState instanceof ShulkerBox) { - return isShulkerBlocked(block); - } - - if (!(blockState instanceof org.bukkit.block.Chest)) { - return false; - } - - if (isChestBlocked(block)) { - return true; - } - - BlockData blockData = block.getBlockData(); - if (!(blockData instanceof Chest chest) || chest.getType() == Chest.Type.SINGLE) { - return false; - } - - BlockFace relativeFace = switch (chest.getFacing()) { - case NORTH -> chest.getType() == Chest.Type.RIGHT ? BlockFace.WEST : BlockFace.EAST; - case EAST -> chest.getType() == Chest.Type.RIGHT ? BlockFace.NORTH : BlockFace.SOUTH; - case SOUTH -> chest.getType() == Chest.Type.RIGHT ? BlockFace.EAST : BlockFace.WEST; - case WEST -> chest.getType() == Chest.Type.RIGHT ? BlockFace.SOUTH : BlockFace.NORTH; - default -> BlockFace.SELF; - }; - Block relative = block.getRelative(relativeFace); - - if (relative.getType() != block.getType()) { - return false; - } - - BlockData relativeData = relative.getBlockData(); - if (!(relativeData instanceof Chest relativeChest)) { - return false; - } - - if (relativeChest.getFacing() != chest.getFacing() - || relativeChest.getType() != (chest.getType() == Chest.Type.RIGHT ? Chest.Type.LEFT : Chest.Type.RIGHT)) { - return false; - } - - return isChestBlocked(relative); - } + boolean isAnyContainerNeeded(@NotNull Block block); /** * Check if a shulker box block cannot be opened under ordinary circumstances. @@ -127,9 +64,13 @@ default boolean isAnyContainerNeeded(@NotNull Block block) { default boolean isShulkerBlocked(@NotNull Block shulkerBox) { Directional directional = (Directional) shulkerBox.getBlockData(); BlockFace facing = directional.getFacing(); + // Construct a new 1-block bounding box at the origin. BoundingBox box = new BoundingBox(0, 0, 0, 1, 1, 1); + // Expand the box in the direction the shulker will open. box.expand(facing, 0.5); + // Move the box away from the origin by a block so only the expansion intersects with a box around the origin. box.shift(facing.getOppositeFace().getDirection()); + // Check if the relative block's collision shape (which will be at the origin) intersects with the expanded box. return shulkerBox.getRelative(facing).getCollisionShape().overlaps(box); } @@ -151,9 +92,7 @@ default boolean isChestBlocked(@NotNull Block chest) { * @param block the potential container * @return true if the type is a supported container */ - default boolean isAnySilentContainer(@NotNull Block block) { - return isAnySilentContainer(getBlockState(block)); - } + boolean isAnySilentContainer(@NotNull Block block); /** * Check if the given {@link BlockState} is a container which can be unblocked or silenced. diff --git a/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java b/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java index 0ac69779..466cae61 100644 --- a/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java +++ b/api/src/main/java/com/lishid/openinv/util/InventoryAccess.java @@ -20,42 +20,16 @@ import com.lishid.openinv.internal.ISpecialEnderChest; import com.lishid.openinv.internal.ISpecialInventory; import com.lishid.openinv.internal.ISpecialPlayerInventory; -import org.bukkit.block.Block; -import org.bukkit.block.BlockState; import org.bukkit.inventory.Inventory; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.function.BiFunction; public final class InventoryAccess { private static @Nullable BiFunction, ISpecialInventory> provider; - private static Method blockGetStateNoSnapshotMethod; - - static { - try { - blockGetStateNoSnapshotMethod = Block.class.getMethod("getState", boolean.class); - } catch (NoSuchMethodException e) { - blockGetStateNoSnapshotMethod = null; - } - } - - public static BlockState getBlockState(Block block) { - // Try to get block state without snapshot (only available in paper currently) - if (blockGetStateNoSnapshotMethod != null) { - try { - return (BlockState) blockGetStateNoSnapshotMethod.invoke(block, false); - } catch (InvocationTargetException | IllegalAccessException e) { - return block.getState(); - } - } else { - return block.getState(); - } - } public static boolean isUsable() { return provider != null; diff --git a/common/src/main/java/com/lishid/openinv/internal/AnySilentContainerBase.java b/common/src/main/java/com/lishid/openinv/internal/AnySilentContainerBase.java new file mode 100644 index 00000000..df477629 --- /dev/null +++ b/common/src/main/java/com/lishid/openinv/internal/AnySilentContainerBase.java @@ -0,0 +1,106 @@ +package com.lishid.openinv.internal; + +import org.bukkit.block.Barrel; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.block.EnderChest; +import org.bukkit.block.ShulkerBox; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.type.Chest; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Method; + +public abstract class AnySilentContainerBase implements IAnySilentContainer { + + private static final @Nullable Method BLOCK_GET_STATE_BOOLEAN; + + static { + @Nullable Method getState; + try { + //noinspection JavaReflectionMemberAccess + getState = Block.class.getMethod("getState", boolean.class); + } catch (NoSuchMethodException e) { + getState = null; + } + BLOCK_GET_STATE_BOOLEAN = getState; + } + + private static BlockState getBlockState(Block block) { + // Paper: Get state without snapshotting. + if (BLOCK_GET_STATE_BOOLEAN != null) { + try { + return (BlockState) BLOCK_GET_STATE_BOOLEAN.invoke(block, false); + } catch (ReflectiveOperationException ignored) { + // If we encounter an issue, fall through to regular snapshotting method. + } + } + return block.getState(); + } + + @Override + public boolean isAnyContainerNeeded(@NotNull Block block) { + BlockState blockState = getBlockState(block); + + // Barrels do not require AnyContainer. + if (blockState instanceof Barrel) { + return false; + } + + // Enderchests require a non-occluding block on top to open. + if (blockState instanceof EnderChest) { + return block.getRelative(0, 1, 0).getType().isOccluding(); + } + + // Shulker boxes require half a block clear in the direction they open. + if (blockState instanceof ShulkerBox) { + return isShulkerBlocked(block); + } + + if (!(blockState instanceof org.bukkit.block.Chest)) { + return false; + } + + if (isChestBlocked(block)) { + return true; + } + + BlockData blockData = block.getBlockData(); + if (!(blockData instanceof Chest chest) || chest.getType() == Chest.Type.SINGLE) { + return false; + } + + BlockFace relativeFace = switch (chest.getFacing()) { + case NORTH -> chest.getType() == Chest.Type.RIGHT ? BlockFace.WEST : BlockFace.EAST; + case EAST -> chest.getType() == Chest.Type.RIGHT ? BlockFace.NORTH : BlockFace.SOUTH; + case SOUTH -> chest.getType() == Chest.Type.RIGHT ? BlockFace.EAST : BlockFace.WEST; + case WEST -> chest.getType() == Chest.Type.RIGHT ? BlockFace.SOUTH : BlockFace.NORTH; + default -> BlockFace.SELF; + }; + Block relative = block.getRelative(relativeFace); + + if (relative.getType() != block.getType()) { + return false; + } + + BlockData relativeData = relative.getBlockData(); + if (!(relativeData instanceof Chest relativeChest)) { + return false; + } + + if (relativeChest.getFacing() != chest.getFacing() + || relativeChest.getType() != (chest.getType() == Chest.Type.RIGHT ? Chest.Type.LEFT : Chest.Type.RIGHT)) { + return false; + } + + return isChestBlocked(relative); + } + + @Override + public boolean isAnySilentContainer(@NotNull Block block) { + return isAnySilentContainer(getBlockState(block)); + } + +} diff --git a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java index 403b3ed8..aaddac01 100644 --- a/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java +++ b/internal/v1_20_R3/src/main/java/com/lishid/openinv/internal/v1_20_R3/AnySilentContainer.java @@ -16,7 +16,7 @@ package com.lishid.openinv.internal.v1_20_R3; -import com.lishid.openinv.internal.IAnySilentContainer; +import com.lishid.openinv.internal.AnySilentContainerBase; import com.lishid.openinv.util.ReflectionHelper; import com.lishid.openinv.util.lang.LanguageManager; import net.minecraft.core.BlockPos; @@ -48,7 +48,7 @@ import java.lang.reflect.Field; import java.util.logging.Logger; -public class AnySilentContainer implements IAnySilentContainer { +public class AnySilentContainer extends AnySilentContainerBase { private final @NotNull Logger logger; private final @NotNull LanguageManager lang; diff --git a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/AnySilentContainer.java b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/AnySilentContainer.java index f04de59b..9ea9065f 100644 --- a/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/AnySilentContainer.java +++ b/internal/v1_20_R4/src/main/java/com/lishid/openinv/internal/v1_20_R4/AnySilentContainer.java @@ -16,7 +16,7 @@ package com.lishid.openinv.internal.v1_20_R4; -import com.lishid.openinv.internal.IAnySilentContainer; +import com.lishid.openinv.internal.AnySilentContainerBase; import com.lishid.openinv.util.ReflectionHelper; import com.lishid.openinv.util.lang.LanguageManager; import net.minecraft.core.BlockPos; @@ -48,7 +48,7 @@ import java.lang.reflect.Field; import java.util.logging.Logger; -public class AnySilentContainer implements IAnySilentContainer { +public class AnySilentContainer extends AnySilentContainerBase { private final @NotNull Logger logger; private final @NotNull LanguageManager lang; diff --git a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/AnySilentContainer.java b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/AnySilentContainer.java index d5b91cf2..f23f1edc 100644 --- a/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/AnySilentContainer.java +++ b/internal/v1_21_R1/src/main/java/com/lishid/openinv/internal/v1_21_R1/container/AnySilentContainer.java @@ -16,7 +16,7 @@ package com.lishid.openinv.internal.v1_21_R1.container; -import com.lishid.openinv.internal.IAnySilentContainer; +import com.lishid.openinv.internal.AnySilentContainerBase; import com.lishid.openinv.internal.v1_21_R1.container.menu.OpenChestMenu; import com.lishid.openinv.internal.v1_21_R1.player.PlayerManager; import com.lishid.openinv.util.ReflectionHelper; @@ -50,7 +50,7 @@ import java.lang.reflect.Field; import java.util.logging.Logger; -public class AnySilentContainer implements IAnySilentContainer { +public class AnySilentContainer extends AnySilentContainerBase { private final @NotNull Logger logger; private final @NotNull LanguageManager lang;