From e2f3944d66949d766f9bc883e78c3579c261a8ad Mon Sep 17 00:00:00 2001 From: TheBusyBiscuit Date: Tue, 9 Feb 2021 17:08:49 +0100 Subject: [PATCH] Fixes #2817 --- CHANGELOG.md | 1 + .../items/blocks/BlockPlacer.java | 131 +++++++++++------- 2 files changed, 80 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb99aaf7d..84befbac6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ * Fixed #2809 * Fixed #2810 * Fixed #2804 +* Fixed #2817 * Fixed a small exception which gets thrown when Slimefun is disabled due to an invalid environment ## Release Candidate 20 (30 Jan 2021) diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/BlockPlacer.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/BlockPlacer.java index f5800321d..ea384e019 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/BlockPlacer.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/BlockPlacer.java @@ -3,6 +3,9 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.blocks; import java.util.List; import java.util.UUID; +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; + import org.bukkit.Bukkit; import org.bukkit.Effect; import org.bukkit.Location; @@ -13,10 +16,10 @@ import org.bukkit.block.Block; import org.bukkit.block.Dispenser; import org.bukkit.entity.Player; import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; -import io.github.thebusybiscuit.cscorelib2.item.CustomItem; import io.github.thebusybiscuit.cscorelib2.protection.ProtectableAction; import io.github.thebusybiscuit.slimefun4.api.events.BlockPlacerPlaceEvent; import io.github.thebusybiscuit.slimefun4.api.items.ItemSetting; @@ -47,15 +50,17 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; */ public class BlockPlacer extends SlimefunItem { - private final ItemSetting> blacklist = new MaterialTagSetting("unplaceable-blocks", SlimefunTag.UNBREAKABLE_MATERIALS); + private final ItemSetting> unplaceableBlocks = new MaterialTagSetting("unplaceable-blocks", SlimefunTag.UNBREAKABLE_MATERIALS); + @ParametersAreNonnullByDefault public BlockPlacer(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(category, item, recipeType, recipe); - addItemSetting(blacklist); + addItemSetting(unplaceableBlocks); addItemHandler(onPlace(), onBlockDispense()); } + @Nonnull private BlockPlaceHandler onPlace() { return new BlockPlaceHandler(false) { @@ -69,6 +74,7 @@ public class BlockPlacer extends SlimefunItem { }; } + @Nonnull private BlockDispenseHandler onBlockDispense() { return (e, dispenser, facedBlock, machine) -> { if (!hasPermission(dispenser, facedBlock)) { @@ -79,7 +85,7 @@ public class BlockPlacer extends SlimefunItem { Material material = e.getItem().getType(); if (SlimefunTag.SHULKER_BOXES.isTagged(material)) { - /** + /* * Since vanilla Dispensers can already place Shulker boxes, * we simply fallback to the vanilla behaviour. */ @@ -89,7 +95,7 @@ public class BlockPlacer extends SlimefunItem { e.setCancelled(true); if (!material.isBlock() || SlimefunTag.BLOCK_PLACER_IGNORED_MATERIALS.isTagged(material)) { - /** + /* * Some materials cannot be reliably placed, like beds, * it would look kinda wonky, so we just ignore these altogether. * The event has already been cancelled too, so they won't drop. @@ -97,7 +103,7 @@ public class BlockPlacer extends SlimefunItem { return; } - if (facedBlock.isEmpty() && !isBlacklisted(material) && dispenser.getInventory().getViewers().isEmpty()) { + if (facedBlock.isEmpty() && isAllowed(material) && dispenser.getInventory().getViewers().isEmpty()) { SlimefunItem item = SlimefunItem.getByItem(e.getItem()); if (item != null) { @@ -123,11 +129,12 @@ public class BlockPlacer extends SlimefunItem { * * @return Whether this action is permitted or not */ + @ParametersAreNonnullByDefault private boolean hasPermission(Dispenser dispenser, Block target) { String owner = BlockStorage.getLocationInfo(dispenser.getLocation(), "owner"); if (owner == null) { - /** + /* * If no owner was set, then we will fallback to the previous behaviour: * Allowing block placers to bypass protection, newly placed Block placers * will respect protection plugins. @@ -135,20 +142,30 @@ public class BlockPlacer extends SlimefunItem { return true; } + // Get the corresponding OfflinePlayer OfflinePlayer player = Bukkit.getOfflinePlayer(UUID.fromString(owner)); return SlimefunPlugin.getProtectionManager().hasPermission(player, target, ProtectableAction.PLACE_BLOCK); } - private boolean isBlacklisted(Material type) { - for (String blockType : blacklist.getValue()) { + /** + * This checks if the given {@link Material} is allowed to be placed. + * + * @param type + * The {@link Material} to check + * + * @return Whether placing this {@link Material} is allowed + */ + private boolean isAllowed(@Nonnull Material type) { + for (String blockType : unplaceableBlocks.getValue()) { if (type.toString().equals(blockType)) { - return true; + return false; } } - return false; + return true; } + @ParametersAreNonnullByDefault private void placeSlimefunBlock(SlimefunItem sfItem, ItemStack item, Block block, Dispenser dispenser) { BlockPlacerPlaceEvent e = new BlockPlacerPlaceEvent(dispenser.getBlock(), item, block); Bukkit.getPluginManager().callEvent(e); @@ -156,68 +173,78 @@ public class BlockPlacer extends SlimefunItem { if (!e.isCancelled()) { boolean hasItemHandler = sfItem.callItemHandler(BlockPlaceHandler.class, handler -> { if (handler.isBlockPlacerAllowed()) { - block.setType(item.getType()); - block.getWorld().playEffect(block.getLocation(), Effect.STEP_SOUND, item.getType()); + schedulePlacement(block, dispenser.getInventory(), item, () -> { + block.setType(item.getType()); + BlockStorage.store(block, sfItem.getId()); - BlockStorage.store(block, sfItem.getId()); - handler.onBlockPlacerPlace(e); - - if (dispenser.getInventory().containsAtLeast(item, 2)) { - dispenser.getInventory().removeItem(new CustomItem(item, 1)); - } else { - SlimefunPlugin.runSync(() -> dispenser.getInventory().removeItem(item), 2L); - } + handler.onBlockPlacerPlace(e); + }); } }); if (!hasItemHandler) { - block.setType(item.getType()); - block.getWorld().playEffect(block.getLocation(), Effect.STEP_SOUND, item.getType()); - - BlockStorage.store(block, sfItem.getId()); - - if (dispenser.getInventory().containsAtLeast(item, 2)) { - dispenser.getInventory().removeItem(new CustomItem(item, 1)); - } else { - SlimefunPlugin.runSync(() -> dispenser.getInventory().removeItem(item), 2L); - } + schedulePlacement(block, dispenser.getInventory(), item, () -> { + block.setType(item.getType()); + BlockStorage.store(block, sfItem.getId()); + }); } } } + @ParametersAreNonnullByDefault private void placeBlock(ItemStack item, Block facedBlock, Dispenser dispenser) { BlockPlacerPlaceEvent e = new BlockPlacerPlaceEvent(dispenser.getBlock(), item, facedBlock); Bukkit.getPluginManager().callEvent(e); if (!e.isCancelled()) { - facedBlock.setType(item.getType()); + schedulePlacement(facedBlock, dispenser.getInventory(), item, () -> { + facedBlock.setType(item.getType()); - if (item.hasItemMeta()) { - ItemMeta meta = item.getItemMeta(); + if (item.hasItemMeta()) { + ItemMeta meta = item.getItemMeta(); - if (meta.hasDisplayName()) { - BlockStateSnapshotResult blockState = PaperLib.getBlockState(facedBlock, false); + if (meta.hasDisplayName()) { + BlockStateSnapshotResult blockState = PaperLib.getBlockState(facedBlock, false); - if ((blockState.getState() instanceof Nameable)) { - Nameable nameable = ((Nameable) blockState.getState()); - nameable.setCustomName(meta.getDisplayName()); + if ((blockState.getState() instanceof Nameable)) { + Nameable nameable = ((Nameable) blockState.getState()); + nameable.setCustomName(meta.getDisplayName()); - if (blockState.isSnapshot()) { - // Update block state after changing name - blockState.getState().update(true, false); + if (blockState.isSnapshot()) { + // Update block state after changing name + blockState.getState().update(true, false); + } } } + } - - } - - facedBlock.getWorld().playEffect(facedBlock.getLocation(), Effect.STEP_SOUND, item.getType()); - - if (dispenser.getInventory().containsAtLeast(item, 2)) { - dispenser.getInventory().removeItem(new CustomItem(item, 1)); - } else { - SlimefunPlugin.runSync(() -> dispenser.getInventory().removeItem(item), 2L); - } + }); } } + + @ParametersAreNonnullByDefault + private void schedulePlacement(Block b, Inventory inv, ItemStack item, Runnable runnable) { + // We need to delay this due to Dispenser-Inventory synchronization issues in Spigot. + SlimefunPlugin.runSync(() -> { + // Make sure the Block has not been occupied yet + if (b.isEmpty()) { + // Only remove 1 item. + ItemStack removedItem = item.clone(); + removedItem.setAmount(1); + + // Play particles + b.getWorld().playEffect(b.getLocation(), Effect.STEP_SOUND, item.getType()); + + // Make sure the item was actually removed (fixes #2817) + + try { + if (inv.removeItem(removedItem).isEmpty()) { + runnable.run(); + } + } catch (Exception x) { + error("An Exception was thrown while a BlockPlacer was performing its action", x); + } + } + }, 2L); + } }