diff options
Diffstat (limited to 'patch-remap/mache-vineflower/net/minecraft/server/level/ServerPlayerGameMode.java.patch')
-rw-r--r-- | patch-remap/mache-vineflower/net/minecraft/server/level/ServerPlayerGameMode.java.patch | 617 |
1 files changed, 617 insertions, 0 deletions
diff --git a/patch-remap/mache-vineflower/net/minecraft/server/level/ServerPlayerGameMode.java.patch b/patch-remap/mache-vineflower/net/minecraft/server/level/ServerPlayerGameMode.java.patch new file mode 100644 index 0000000000..c2f18c5797 --- /dev/null +++ b/patch-remap/mache-vineflower/net/minecraft/server/level/ServerPlayerGameMode.java.patch @@ -0,0 +1,617 @@ +--- a/net/minecraft/server/level/ServerPlayerGameMode.java ++++ b/net/minecraft/server/level/ServerPlayerGameMode.java +@@ -9,40 +9,65 @@ + import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket; + import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket; + import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; +-import net.minecraft.server.network.ServerGamePacketListenerImpl; +-import net.minecraft.world.InteractionHand; ++import net.minecraft.world.EnumHand; ++import net.minecraft.world.ITileInventory; + import net.minecraft.world.InteractionResult; + import net.minecraft.world.InteractionResultHolder; +-import net.minecraft.world.MenuProvider; ++import net.minecraft.world.entity.EquipmentSlot; ++import net.minecraft.world.item.DoubleHighBlockItem; + import net.minecraft.world.item.ItemStack; + import net.minecraft.world.item.context.UseOnContext; + import net.minecraft.world.level.GameType; + import net.minecraft.world.level.Level; + import net.minecraft.world.level.block.Block; + import net.minecraft.world.level.block.GameMasterBlock; ++import net.minecraft.world.level.block.TrapDoorBlock; + import net.minecraft.world.level.block.entity.BlockEntity; +-import net.minecraft.world.level.block.state.BlockState; ++import net.minecraft.world.level.block.state.IBlockData; ++import org.slf4j.Logger; ++ ++// CraftBukkit start ++import java.util.ArrayList; ++import net.minecraft.server.MinecraftServer; ++import net.minecraft.server.network.ServerGamePacketListenerImpl; ++import net.minecraft.world.level.block.Blocks; ++import net.minecraft.world.level.block.CakeBlock; ++import net.minecraft.world.level.block.DoorBlock; ++import net.minecraft.world.level.block.state.properties.BlockPropertyDoubleBlockHalf; + import net.minecraft.world.phys.BlockHitResult; + import net.minecraft.world.phys.Vec3; +-import org.slf4j.Logger; ++import org.bukkit.GameMode; ++import org.bukkit.craftbukkit.block.CraftBlock; ++import org.bukkit.event.block.BlockBreakEvent; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.Event; ++import org.bukkit.event.block.Action; ++import org.bukkit.event.player.PlayerGameModeChangeEvent; ++import org.bukkit.event.player.PlayerInteractEvent; ++// CraftBukkit end + + public class ServerPlayerGameMode { ++ + private static final Logger LOGGER = LogUtils.getLogger(); + protected ServerLevel level; + protected final ServerPlayer player; +- private GameType gameModeForPlayer = GameType.DEFAULT_MODE; ++ private GameType gameModeForPlayer; + @Nullable + private GameType previousGameModeForPlayer; + private boolean isDestroyingBlock; + private int destroyProgressStart; +- private BlockPos destroyPos = BlockPos.ZERO; ++ private BlockPos destroyPos; + private int gameTicks; + private boolean hasDelayedDestroy; +- private BlockPos delayedDestroyPos = BlockPos.ZERO; ++ private BlockPos delayedDestroyPos; + private int delayedTickStart; +- private int lastSentState = -1; ++ private int lastSentState; + + public ServerPlayerGameMode(ServerPlayer player) { ++ this.gameModeForPlayer = GameType.DEFAULT_MODE; ++ this.destroyPos = BlockPos.ZERO; ++ this.delayedDestroyPos = BlockPos.ZERO; ++ this.lastSentState = -1; + this.player = player; + this.level = player.serverLevel(); + } +@@ -51,12 +76,16 @@ + if (gameModeForPlayer == this.gameModeForPlayer) { + return false; + } else { ++ // CraftBukkit start ++ PlayerGameModeChangeEvent event = new PlayerGameModeChangeEvent(player.getBukkitEntity(), GameMode.getByValue(gameModeForPlayer.getId())); ++ level.getCraftServer().getPluginManager().callEvent(event); ++ if (event.isCancelled()) { ++ return false; ++ } ++ // CraftBukkit end + this.setGameModeForPlayer(gameModeForPlayer, this.previousGameModeForPlayer); + this.player.onUpdateAbilities(); +- this.player +- .server +- .getPlayerList() +- .broadcastAll(new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE, this.player)); ++ this.player.server.getPlayerList().broadcastAll(new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE, this.player), this.player); // CraftBukkit + this.level.updateSleepingPlayerList(); + return true; + } +@@ -86,59 +115,87 @@ + } + + public void tick() { +- this.gameTicks++; ++ this.gameTicks = MinecraftServer.currentTick; // CraftBukkit; ++ IBlockData iblockdata; ++ + if (this.hasDelayedDestroy) { +- BlockState blockState = this.level.getBlockState(this.delayedDestroyPos); +- if (blockState.isAir()) { ++ iblockdata = this.level.getBlockState(this.delayedDestroyPos); ++ if (iblockdata.isAir()) { + this.hasDelayedDestroy = false; + } else { +- float f = this.incrementDestroyProgress(blockState, this.delayedDestroyPos, this.delayedTickStart); ++ float f = this.incrementDestroyProgress(iblockdata, this.delayedDestroyPos, this.delayedTickStart); ++ + if (f >= 1.0F) { + this.hasDelayedDestroy = false; + this.destroyBlock(this.delayedDestroyPos); + } + } + } else if (this.isDestroyingBlock) { +- BlockState blockState = this.level.getBlockState(this.destroyPos); +- if (blockState.isAir()) { ++ iblockdata = this.level.getBlockState(this.destroyPos); ++ if (iblockdata.isAir()) { + this.level.destroyBlockProgress(this.player.getId(), this.destroyPos, -1); + this.lastSentState = -1; + this.isDestroyingBlock = false; + } else { +- this.incrementDestroyProgress(blockState, this.destroyPos, this.destroyProgressStart); ++ this.incrementDestroyProgress(iblockdata, this.destroyPos, this.destroyProgressStart); + } + } ++ + } + +- private float incrementDestroyProgress(BlockState state, BlockPos pos, int startTick) { +- int i = this.gameTicks - startTick; +- float f = state.getDestroyProgress(this.player, this.player.level(), pos) * (float)(i + 1); +- int i1 = (int)(f * 10.0F); +- if (i1 != this.lastSentState) { +- this.level.destroyBlockProgress(this.player.getId(), pos, i1); +- this.lastSentState = i1; ++ private float incrementDestroyProgress(IBlockData state, BlockPos pos, int startTick) { ++ int j = this.gameTicks - startTick; ++ float f = state.getDestroyProgress(this.player, this.player.level(), pos) * (float) (j + 1); ++ int k = (int) (f * 10.0F); ++ ++ if (k != this.lastSentState) { ++ this.level.destroyBlockProgress(this.player.getId(), pos, k); ++ this.lastSentState = k; + } + + return f; + } + +- private void debugLogging(BlockPos pos, boolean flag, int sequence, String message) { +- } ++ private void debugLogging(BlockPos pos, boolean flag, int sequence, String message) {} + +- public void handleBlockBreakAction(BlockPos pos, ServerboundPlayerActionPacket.Action action, Direction face, int maxBuildHeight, int sequence) { ++ public void handleBlockBreakAction(BlockPos pos, ServerboundPlayerActionPacket.EnumPlayerDigType action, Direction face, int maxBuildHeight, int sequence) { + if (this.player.getEyePosition().distanceToSqr(Vec3.atCenterOf(pos)) > ServerGamePacketListenerImpl.MAX_INTERACTION_DISTANCE) { + this.debugLogging(pos, false, sequence, "too far"); + } else if (pos.getY() >= maxBuildHeight) { + this.player.connection.send(new ClientboundBlockUpdatePacket(pos, this.level.getBlockState(pos))); + this.debugLogging(pos, false, sequence, "too high"); + } else { +- if (action == ServerboundPlayerActionPacket.Action.START_DESTROY_BLOCK) { ++ IBlockData iblockdata; ++ ++ if (action == ServerboundPlayerActionPacket.EnumPlayerDigType.START_DESTROY_BLOCK) { + if (!this.level.mayInteract(this.player, pos)) { ++ // CraftBukkit start - fire PlayerInteractEvent ++ CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_BLOCK, pos, face, this.player.getInventory().getSelected(), EnumHand.MAIN_HAND); + this.player.connection.send(new ClientboundBlockUpdatePacket(pos, this.level.getBlockState(pos))); + this.debugLogging(pos, false, sequence, "may not interact"); ++ // Update any tile entity data for this block ++ BlockEntity tileentity = level.getBlockEntity(pos); ++ if (tileentity != null) { ++ this.player.connection.send(tileentity.getUpdatePacket()); ++ } ++ // CraftBukkit end + return; + } + ++ // CraftBukkit start ++ PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_BLOCK, pos, face, this.player.getInventory().getSelected(), EnumHand.MAIN_HAND); ++ if (event.isCancelled()) { ++ // Let the client know the block still exists ++ this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos)); ++ // Update any tile entity data for this block ++ BlockEntity tileentity = this.level.getBlockEntity(pos); ++ if (tileentity != null) { ++ this.player.connection.send(tileentity.getUpdatePacket()); ++ } ++ return; ++ } ++ // CraftBukkit end ++ + if (this.isCreative()) { + this.destroyAndAck(pos, sequence, "creative destroy"); + return; +@@ -152,13 +209,46 @@ + + this.destroyProgressStart = this.gameTicks; + float f = 1.0F; +- BlockState blockState = this.level.getBlockState(pos); +- if (!blockState.isAir()) { +- blockState.attack(this.level, pos, this.player); +- f = blockState.getDestroyProgress(this.player, this.player.level(), pos); ++ ++ iblockdata = this.level.getBlockState(pos); ++ // CraftBukkit start - Swings at air do *NOT* exist. ++ if (event.useInteractedBlock() == Event.Result.DENY) { ++ // If we denied a door from opening, we need to send a correcting update to the client, as it already opened the door. ++ IBlockData data = this.level.getBlockState(pos); ++ if (data.getBlock() instanceof DoorBlock) { ++ // For some reason *BOTH* the bottom/top part have to be marked updated. ++ boolean bottom = data.getValue(DoorBlock.HALF) == BlockPropertyDoubleBlockHalf.LOWER; ++ this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos)); ++ this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, bottom ? pos.above() : pos.below())); ++ } else if (data.getBlock() instanceof TrapDoorBlock) { ++ this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos)); ++ } ++ } else if (!iblockdata.isAir()) { ++ iblockdata.attack(this.level, pos, this.player); ++ f = iblockdata.getDestroyProgress(this.player, this.player.level(), pos); + } + +- if (!blockState.isAir() && f >= 1.0F) { ++ if (event.useItemInHand() == Event.Result.DENY) { ++ // If we 'insta destroyed' then the client needs to be informed. ++ if (f > 1.0f) { ++ this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos)); ++ } ++ return; ++ } ++ org.bukkit.event.block.BlockDamageEvent blockEvent = CraftEventFactory.callBlockDamageEvent(this.player, pos, this.player.getInventory().getSelected(), f >= 1.0f); ++ ++ if (blockEvent.isCancelled()) { ++ // Let the client know the block still exists ++ this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos)); ++ return; ++ } ++ ++ if (blockEvent.getInstaBreak()) { ++ f = 2.0f; ++ } ++ // CraftBukkit end ++ ++ if (!iblockdata.isAir() && f >= 1.0F) { + this.destroyAndAck(pos, sequence, "insta mine"); + } else { + if (this.isDestroyingBlock) { +@@ -168,17 +258,20 @@ + + this.isDestroyingBlock = true; + this.destroyPos = pos.immutable(); +- int i = (int)(f * 10.0F); +- this.level.destroyBlockProgress(this.player.getId(), pos, i); ++ int k = (int) (f * 10.0F); ++ ++ this.level.destroyBlockProgress(this.player.getId(), pos, k); + this.debugLogging(pos, true, sequence, "actual start of destroying"); +- this.lastSentState = i; ++ this.lastSentState = k; + } +- } else if (action == ServerboundPlayerActionPacket.Action.STOP_DESTROY_BLOCK) { ++ } else if (action == ServerboundPlayerActionPacket.EnumPlayerDigType.STOP_DESTROY_BLOCK) { + if (pos.equals(this.destroyPos)) { +- int i1 = this.gameTicks - this.destroyProgressStart; +- BlockState blockStatex = this.level.getBlockState(pos); +- if (!blockStatex.isAir()) { +- float f1 = blockStatex.getDestroyProgress(this.player, this.player.level(), pos) * (float)(i1 + 1); ++ int l = this.gameTicks - this.destroyProgressStart; ++ ++ iblockdata = this.level.getBlockState(pos); ++ if (!iblockdata.isAir()) { ++ float f1 = iblockdata.getDestroyProgress(this.player, this.player.level(), pos) * (float) (l + 1); ++ + if (f1 >= 0.7F) { + this.isDestroyingBlock = false; + this.level.destroyBlockProgress(this.player.getId(), pos, -1); +@@ -196,17 +289,20 @@ + } + + this.debugLogging(pos, true, sequence, "stopped destroying"); +- } else if (action == ServerboundPlayerActionPacket.Action.ABORT_DESTROY_BLOCK) { ++ } else if (action == ServerboundPlayerActionPacket.EnumPlayerDigType.ABORT_DESTROY_BLOCK) { + this.isDestroyingBlock = false; + if (!Objects.equals(this.destroyPos, pos)) { +- LOGGER.warn("Mismatch in destroy block pos: {} {}", this.destroyPos, pos); ++ ServerPlayerGameMode.LOGGER.debug("Mismatch in destroy block pos: {} {}", this.destroyPos, pos); // CraftBukkit - SPIGOT-5457 sent by client when interact event cancelled + this.level.destroyBlockProgress(this.player.getId(), this.destroyPos, -1); + this.debugLogging(pos, true, sequence, "aborted mismatched destroying"); + } + + this.level.destroyBlockProgress(this.player.getId(), pos, -1); + this.debugLogging(pos, true, sequence, "aborted destroying"); ++ ++ CraftEventFactory.callBlockDamageAbortEvent(this.player, pos, this.player.getInventory().getSelected()); // CraftBukkit + } ++ + } + } + +@@ -217,71 +313,149 @@ + this.player.connection.send(new ClientboundBlockUpdatePacket(pos, this.level.getBlockState(pos))); + this.debugLogging(pos, false, sequence, message); + } ++ + } + + public boolean destroyBlock(BlockPos pos) { +- BlockState blockState = this.level.getBlockState(pos); +- if (!this.player.getMainHandItem().getItem().canAttackBlock(blockState, this.level, pos, this.player)) { ++ IBlockData iblockdata = this.level.getBlockState(pos); ++ // CraftBukkit start - fire BlockBreakEvent ++ org.bukkit.block.Block bblock = CraftBlock.at(level, pos); ++ BlockBreakEvent event = null; ++ ++ if (this.player instanceof ServerPlayer) { ++ // Sword + Creative mode pre-cancel ++ boolean isSwordNoBreak = !this.player.getMainHandItem().getItem().canAttackBlock(iblockdata, this.level, pos, this.player); ++ ++ // Tell client the block is gone immediately then process events ++ // Don't tell the client if its a creative sword break because its not broken! ++ if (level.getBlockEntity(pos) == null && !isSwordNoBreak) { ++ ClientboundBlockUpdatePacket packet = new ClientboundBlockUpdatePacket(pos, Blocks.AIR.defaultBlockState()); ++ this.player.connection.send(packet); ++ } ++ ++ event = new BlockBreakEvent(bblock, this.player.getBukkitEntity()); ++ ++ // Sword + Creative mode pre-cancel ++ event.setCancelled(isSwordNoBreak); ++ ++ // Calculate default block experience ++ IBlockData nmsData = this.level.getBlockState(pos); ++ Block nmsBlock = nmsData.getBlock(); ++ ++ ItemStack itemstack = this.player.getItemBySlot(EquipmentSlot.MAINHAND); ++ ++ if (nmsBlock != null && !event.isCancelled() && !this.isCreative() && this.player.hasCorrectToolForDrops(nmsBlock.defaultBlockState())) { ++ event.setExpToDrop(nmsBlock.getExpDrop(nmsData, this.level, pos, itemstack, true)); ++ } ++ ++ this.level.getCraftServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) { ++ if (isSwordNoBreak) { ++ return false; ++ } ++ // Let the client know the block still exists ++ this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos)); ++ ++ // Brute force all possible updates ++ for (Direction dir : Direction.values()) { ++ this.player.connection.send(new ClientboundBlockUpdatePacket(level, pos.relative(dir))); ++ } ++ ++ // Update any tile entity data for this block ++ BlockEntity tileentity = this.level.getBlockEntity(pos); ++ if (tileentity != null) { ++ this.player.connection.send(tileentity.getUpdatePacket()); ++ } ++ return false; ++ } ++ } ++ // CraftBukkit end ++ ++ if (false && !this.player.getMainHandItem().getItem().canAttackBlock(iblockdata, this.level, pos, this.player)) { // CraftBukkit - false + return false; + } else { +- BlockEntity blockEntity = this.level.getBlockEntity(pos); +- Block block = blockState.getBlock(); ++ iblockdata = this.level.getBlockState(pos); // CraftBukkit - update state from plugins ++ if (iblockdata.isAir()) return false; // CraftBukkit - A plugin set block to air without cancelling ++ BlockEntity tileentity = this.level.getBlockEntity(pos); ++ Block block = iblockdata.getBlock(); ++ + if (block instanceof GameMasterBlock && !this.player.canUseGameMasterBlocks()) { +- this.level.sendBlockUpdated(pos, blockState, blockState, 3); ++ this.level.sendBlockUpdated(pos, iblockdata, iblockdata, 3); + return false; + } else if (this.player.blockActionRestricted(this.level, pos, this.gameModeForPlayer)) { + return false; + } else { +- BlockState blockState1 = block.playerWillDestroy(this.level, pos, blockState, this.player); ++ // CraftBukkit start ++ org.bukkit.block.BlockState state = bblock.getState(); ++ level.captureDrops = new ArrayList<>(); ++ // CraftBukkit end ++ IBlockData iblockdata1 = block.playerWillDestroy(this.level, pos, iblockdata, this.player); + boolean flag = this.level.removeBlock(pos, false); ++ + if (flag) { +- block.destroy(this.level, pos, blockState1); ++ block.destroy(this.level, pos, iblockdata1); + } + + if (this.isCreative()) { +- return true; ++ // return true; // CraftBukkit + } else { +- ItemStack mainHandItem = this.player.getMainHandItem(); +- ItemStack itemStack = mainHandItem.copy(); +- boolean hasCorrectToolForDrops = this.player.hasCorrectToolForDrops(blockState1); +- mainHandItem.mineBlock(this.level, blockState1, pos, this.player); +- if (flag && hasCorrectToolForDrops) { +- block.playerDestroy(this.level, this.player, pos, blockState1, blockEntity, itemStack); ++ ItemStack itemstack = this.player.getMainHandItem(); ++ ItemStack itemstack1 = itemstack.copy(); ++ boolean flag1 = this.player.hasCorrectToolForDrops(iblockdata1); ++ ++ itemstack.mineBlock(this.level, iblockdata1, pos, this.player); ++ if (flag && flag1 && event.isDropItems()) { // CraftBukkit - Check if block should drop items ++ block.playerDestroy(this.level, this.player, pos, iblockdata1, tileentity, itemstack1); + } + +- return true; ++ // return true; // CraftBukkit + } ++ // CraftBukkit start ++ if (event.isDropItems()) { ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDropItemEvent(bblock, state, this.player, level.captureDrops); ++ } ++ level.captureDrops = null; ++ ++ // Drop event experience ++ if (flag && event != null) { ++ iblockdata.getBlock().popExperience(this.level, pos, event.getExpToDrop()); ++ } ++ ++ return true; ++ // CraftBukkit end + } + } + } + +- public InteractionResult useItem(ServerPlayer player, Level level, ItemStack stack, InteractionHand hand) { ++ public InteractionResult useItem(ServerPlayer player, Level level, ItemStack stack, EnumHand hand) { + if (this.gameModeForPlayer == GameType.SPECTATOR) { + return InteractionResult.PASS; + } else if (player.getCooldowns().isOnCooldown(stack.getItem())) { + return InteractionResult.PASS; + } else { +- int count = stack.getCount(); +- int damageValue = stack.getDamageValue(); +- InteractionResultHolder<ItemStack> interactionResultHolder = stack.use(level, player, hand); +- ItemStack itemStack = interactionResultHolder.getObject(); +- if (itemStack == stack && itemStack.getCount() == count && itemStack.getUseDuration() <= 0 && itemStack.getDamageValue() == damageValue) { +- return interactionResultHolder.getResult(); +- } else if (interactionResultHolder.getResult() == InteractionResult.FAIL && itemStack.getUseDuration() > 0 && !player.isUsingItem()) { +- return interactionResultHolder.getResult(); ++ int i = stack.getCount(); ++ int j = stack.getDamageValue(); ++ InteractionResultHolder<ItemStack> interactionresultwrapper = stack.use(level, player, hand); ++ ItemStack itemstack1 = (ItemStack) interactionresultwrapper.getObject(); ++ ++ if (itemstack1 == stack && itemstack1.getCount() == i && itemstack1.getUseDuration() <= 0 && itemstack1.getDamageValue() == j) { ++ return interactionresultwrapper.getResult(); ++ } else if (interactionresultwrapper.getResult() == InteractionResult.FAIL && itemstack1.getUseDuration() > 0 && !player.isUsingItem()) { ++ return interactionresultwrapper.getResult(); + } else { +- if (stack != itemStack) { +- player.setItemInHand(hand, itemStack); ++ if (stack != itemstack1) { ++ player.setItemInHand(hand, itemstack1); + } + +- if (this.isCreative() && itemStack != ItemStack.EMPTY) { +- itemStack.setCount(count); +- if (itemStack.isDamageableItem() && itemStack.getDamageValue() != damageValue) { +- itemStack.setDamageValue(damageValue); ++ if (this.isCreative() && itemstack1 != ItemStack.EMPTY) { ++ itemstack1.setCount(i); ++ if (itemstack1.isDamageableItem() && itemstack1.getDamageValue() != j) { ++ itemstack1.setDamageValue(j); + } + } + +- if (itemStack.isEmpty()) { ++ if (itemstack1.isEmpty()) { + player.setItemInHand(hand, ItemStack.EMPTY); + } + +@@ -289,20 +463,62 @@ + player.inventoryMenu.sendAllDataToRemote(); + } + +- return interactionResultHolder.getResult(); ++ return interactionresultwrapper.getResult(); + } + } + } + +- public InteractionResult useItemOn(ServerPlayer player, Level level, ItemStack stack, InteractionHand hand, BlockHitResult hitResult) { +- BlockPos blockPos = hitResult.getBlockPos(); +- BlockState blockState = level.getBlockState(blockPos); +- if (!blockState.getBlock().isEnabled(level.enabledFeatures())) { ++ // CraftBukkit start - whole method ++ public boolean interactResult = false; ++ public boolean firedInteract = false; ++ public BlockPos interactPosition; ++ public EnumHand interactHand; ++ public ItemStack interactItemStack; ++ public InteractionResult useItemOn(ServerPlayer player, Level level, ItemStack stack, EnumHand hand, BlockHitResult hitResult) { ++ BlockPos blockposition = hitResult.getBlockPos(); ++ IBlockData iblockdata = level.getBlockState(blockposition); ++ InteractionResult enuminteractionresult = InteractionResult.PASS; ++ boolean cancelledBlock = false; ++ ++ if (!iblockdata.getBlock().isEnabled(level.enabledFeatures())) { + return InteractionResult.FAIL; + } else if (this.gameModeForPlayer == GameType.SPECTATOR) { +- MenuProvider menuProvider = blockState.getMenuProvider(level, blockPos); +- if (menuProvider != null) { +- player.openMenu(menuProvider); ++ ITileInventory itileinventory = iblockdata.getMenuProvider(level, blockposition); ++ cancelledBlock = !(itileinventory instanceof ITileInventory); ++ } ++ ++ if (player.getCooldowns().isOnCooldown(stack.getItem())) { ++ cancelledBlock = true; ++ } ++ ++ PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(player, Action.RIGHT_CLICK_BLOCK, blockposition, hitResult.getDirection(), stack, cancelledBlock, hand, hitResult.getLocation()); ++ firedInteract = true; ++ interactResult = event.useItemInHand() == Event.Result.DENY; ++ interactPosition = blockposition.immutable(); ++ interactHand = hand; ++ interactItemStack = stack.copy(); ++ ++ if (event.useInteractedBlock() == Event.Result.DENY) { ++ // If we denied a door from opening, we need to send a correcting update to the client, as it already opened the door. ++ if (iblockdata.getBlock() instanceof DoorBlock) { ++ boolean bottom = iblockdata.getValue(DoorBlock.HALF) == BlockPropertyDoubleBlockHalf.LOWER; ++ player.connection.send(new ClientboundBlockUpdatePacket(level, bottom ? blockposition.above() : blockposition.below())); ++ } else if (iblockdata.getBlock() instanceof CakeBlock) { ++ player.getBukkitEntity().sendHealthUpdate(); // SPIGOT-1341 - reset health for cake ++ } else if (interactItemStack.getItem() instanceof DoubleHighBlockItem) { ++ // send a correcting update to the client, as it already placed the upper half of the bisected item ++ player.connection.send(new ClientboundBlockUpdatePacket(level, blockposition.relative(hitResult.getDirection()).above())); ++ ++ // send a correcting update to the client for the block above as well, this because of replaceable blocks (such as grass, sea grass etc) ++ player.connection.send(new ClientboundBlockUpdatePacket(level, blockposition.above())); ++ } ++ player.getBukkitEntity().updateInventory(); // SPIGOT-2867 ++ enuminteractionresult = (event.useItemInHand() != Event.Result.ALLOW) ? InteractionResult.SUCCESS : InteractionResult.PASS; ++ } else if (this.gameModeForPlayer == GameType.SPECTATOR) { ++ ITileInventory itileinventory = iblockdata.getMenuProvider(level, blockposition); ++ ++ if (itileinventory != null) { ++ player.openMenu(itileinventory); + return InteractionResult.SUCCESS; + } else { + return InteractionResult.PASS; +@@ -310,35 +526,39 @@ + } else { + boolean flag = !player.getMainHandItem().isEmpty() || !player.getOffhandItem().isEmpty(); + boolean flag1 = player.isSecondaryUseActive() && flag; +- ItemStack itemStack = stack.copy(); ++ ItemStack itemstack1 = stack.copy(); ++ + if (!flag1) { +- InteractionResult interactionResult = blockState.use(level, player, hand, hitResult); +- if (interactionResult.consumesAction()) { +- CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger(player, blockPos, itemStack); +- return interactionResult; ++ enuminteractionresult = iblockdata.use(level, player, hand, hitResult); ++ ++ if (enuminteractionresult.consumesAction()) { ++ CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger(player, blockposition, itemstack1); ++ return enuminteractionresult; + } + } + +- if (!stack.isEmpty() && !player.getCooldowns().isOnCooldown(stack.getItem())) { +- UseOnContext useOnContext = new UseOnContext(player, hand, hitResult); +- InteractionResult interactionResult1; ++ if (!stack.isEmpty() && enuminteractionresult != InteractionResult.SUCCESS && !interactResult) { // add !interactResult SPIGOT-764 ++ UseOnContext itemactioncontext = new UseOnContext(player, hand, hitResult); ++ InteractionResult enuminteractionresult1; ++ + if (this.isCreative()) { +- int count = stack.getCount(); +- interactionResult1 = stack.useOn(useOnContext); +- stack.setCount(count); ++ int i = stack.getCount(); ++ ++ enuminteractionresult1 = stack.useOn(itemactioncontext); ++ stack.setCount(i); + } else { +- interactionResult1 = stack.useOn(useOnContext); ++ enuminteractionresult1 = stack.useOn(itemactioncontext); + } + +- if (interactionResult1.consumesAction()) { +- CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger(player, blockPos, itemStack); ++ if (enuminteractionresult1.consumesAction()) { ++ CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger(player, blockposition, itemstack1); + } + +- return interactionResult1; +- } else { +- return InteractionResult.PASS; ++ return enuminteractionresult1; + } + } ++ return enuminteractionresult; ++ // CraftBukkit end + } + + public void setLevel(ServerLevel serverLevel) { |