diff options
Diffstat (limited to 'patch-remap/mache-spigotflower/net/minecraft/server/level/ServerPlayerGameMode.java.patch')
-rw-r--r-- | patch-remap/mache-spigotflower/net/minecraft/server/level/ServerPlayerGameMode.java.patch | 669 |
1 files changed, 669 insertions, 0 deletions
diff --git a/patch-remap/mache-spigotflower/net/minecraft/server/level/ServerPlayerGameMode.java.patch b/patch-remap/mache-spigotflower/net/minecraft/server/level/ServerPlayerGameMode.java.patch new file mode 100644 index 0000000000..78a8dc985d --- /dev/null +++ b/patch-remap/mache-spigotflower/net/minecraft/server/level/ServerPlayerGameMode.java.patch @@ -0,0 +1,669 @@ +--- a/net/minecraft/server/level/ServerPlayerGameMode.java ++++ b/net/minecraft/server/level/ServerPlayerGameMode.java +@@ -9,22 +9,42 @@ + 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 { + +@@ -43,31 +63,38 @@ + private int delayedTickStart; + private int lastSentState; + +- public ServerPlayerGameMode(ServerPlayer serverplayer) { ++ public ServerPlayerGameMode(ServerPlayer player) { + this.gameModeForPlayer = GameType.DEFAULT_MODE; + this.destroyPos = BlockPos.ZERO; + this.delayedDestroyPos = BlockPos.ZERO; + this.lastSentState = -1; +- this.player = serverplayer; +- this.level = serverplayer.serverLevel(); ++ this.player = player; ++ this.level = player.serverLevel(); + } + +- public boolean changeGameModeForPlayer(GameType gametype) { +- if (gametype == this.gameModeForPlayer) { ++ public boolean changeGameModeForPlayer(GameType gameModeForPlayer) { ++ if (gameModeForPlayer == this.gameModeForPlayer) { + return false; + } else { +- this.setGameModeForPlayer(gametype, this.previousGameModeForPlayer); ++ // 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; + } + } + +- protected void setGameModeForPlayer(GameType gametype, @Nullable GameType gametype1) { +- this.previousGameModeForPlayer = gametype1; +- this.gameModeForPlayer = gametype; +- gametype.updatePlayerAbilities(this.player.getAbilities()); ++ protected void setGameModeForPlayer(GameType gameModeForPlayer, @Nullable GameType previousGameModeForPlayer) { ++ this.previousGameModeForPlayer = previousGameModeForPlayer; ++ this.gameModeForPlayer = gameModeForPlayer; ++ gameModeForPlayer.updatePlayerAbilities(this.player.getAbilities()); + } + + public GameType getGameModeForPlayer() { +@@ -88,15 +115,15 @@ + } + + public void tick() { +- ++this.gameTicks; +- BlockState blockstate; ++ this.gameTicks = MinecraftServer.currentTick; // CraftBukkit; ++ IBlockData iblockdata; + + if (this.hasDelayedDestroy) { +- 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; +@@ -104,193 +131,321 @@ + } + } + } else if (this.isDestroyingBlock) { +- 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 blockstate, BlockPos blockpos, int i) { +- int j = this.gameTicks - i; +- float f = blockstate.getDestroyProgress(this.player, this.player.level(), blockpos) * (float) (j + 1); ++ 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(), blockpos, k); ++ this.level.destroyBlockProgress(this.player.getId(), pos, k); + this.lastSentState = k; + } + + return f; + } + +- private void debugLogging(BlockPos blockpos, boolean flag, int i, String s) {} ++ private void debugLogging(BlockPos pos, boolean flag, int sequence, String message) {} + +- public void handleBlockBreakAction(BlockPos blockpos, ServerboundPlayerActionPacket.Action serverboundplayeractionpacket_action, Direction direction, int i, int j) { +- if (this.player.getEyePosition().distanceToSqr(Vec3.atCenterOf(blockpos)) > ServerGamePacketListenerImpl.MAX_INTERACTION_DISTANCE) { +- this.debugLogging(blockpos, false, j, "too far"); +- } else if (blockpos.getY() >= i) { +- this.player.connection.send(new ClientboundBlockUpdatePacket(blockpos, this.level.getBlockState(blockpos))); +- this.debugLogging(blockpos, false, j, "too high"); ++ 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 { +- BlockState blockstate; ++ IBlockData iblockdata; + +- if (serverboundplayeractionpacket_action == ServerboundPlayerActionPacket.Action.START_DESTROY_BLOCK) { +- if (!this.level.mayInteract(this.player, blockpos)) { +- this.player.connection.send(new ClientboundBlockUpdatePacket(blockpos, this.level.getBlockState(blockpos))); +- this.debugLogging(blockpos, false, j, "may not interact"); ++ 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(blockpos, j, "creative destroy"); ++ this.destroyAndAck(pos, sequence, "creative destroy"); + return; + } + +- if (this.player.blockActionRestricted(this.level, blockpos, this.gameModeForPlayer)) { +- this.player.connection.send(new ClientboundBlockUpdatePacket(blockpos, this.level.getBlockState(blockpos))); +- this.debugLogging(blockpos, false, j, "block action restricted"); ++ if (this.player.blockActionRestricted(this.level, pos, this.gameModeForPlayer)) { ++ this.player.connection.send(new ClientboundBlockUpdatePacket(pos, this.level.getBlockState(pos))); ++ this.debugLogging(pos, false, sequence, "block action restricted"); + return; + } + + this.destroyProgressStart = this.gameTicks; + float f = 1.0F; + +- blockstate = this.level.getBlockState(blockpos); +- if (!blockstate.isAir()) { +- blockstate.attack(this.level, blockpos, this.player); +- f = blockstate.getDestroyProgress(this.player, this.player.level(), blockpos); ++ 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) { +- this.destroyAndAck(blockpos, j, "insta mine"); ++ 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) { + this.player.connection.send(new ClientboundBlockUpdatePacket(this.destroyPos, this.level.getBlockState(this.destroyPos))); +- this.debugLogging(blockpos, false, j, "abort destroying since another started (client insta mine, server disagreed)"); ++ this.debugLogging(pos, false, sequence, "abort destroying since another started (client insta mine, server disagreed)"); + } + + this.isDestroyingBlock = true; +- this.destroyPos = blockpos.immutable(); ++ this.destroyPos = pos.immutable(); + int k = (int) (f * 10.0F); + +- this.level.destroyBlockProgress(this.player.getId(), blockpos, k); +- this.debugLogging(blockpos, true, j, "actual start of destroying"); ++ this.level.destroyBlockProgress(this.player.getId(), pos, k); ++ this.debugLogging(pos, true, sequence, "actual start of destroying"); + this.lastSentState = k; + } +- } else if (serverboundplayeractionpacket_action == ServerboundPlayerActionPacket.Action.STOP_DESTROY_BLOCK) { +- if (blockpos.equals(this.destroyPos)) { ++ } else if (action == ServerboundPlayerActionPacket.EnumPlayerDigType.STOP_DESTROY_BLOCK) { ++ if (pos.equals(this.destroyPos)) { + int l = this.gameTicks - this.destroyProgressStart; + +- blockstate = this.level.getBlockState(blockpos); +- if (!blockstate.isAir()) { +- float f1 = blockstate.getDestroyProgress(this.player, this.player.level(), blockpos) * (float) (l + 1); ++ 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(), blockpos, -1); +- this.destroyAndAck(blockpos, j, "destroyed"); ++ this.level.destroyBlockProgress(this.player.getId(), pos, -1); ++ this.destroyAndAck(pos, sequence, "destroyed"); + return; + } + + if (!this.hasDelayedDestroy) { + this.isDestroyingBlock = false; + this.hasDelayedDestroy = true; +- this.delayedDestroyPos = blockpos; ++ this.delayedDestroyPos = pos; + this.delayedTickStart = this.destroyProgressStart; + } + } + } + +- this.debugLogging(blockpos, true, j, "stopped destroying"); +- } else if (serverboundplayeractionpacket_action == ServerboundPlayerActionPacket.Action.ABORT_DESTROY_BLOCK) { ++ this.debugLogging(pos, true, sequence, "stopped destroying"); ++ } else if (action == ServerboundPlayerActionPacket.EnumPlayerDigType.ABORT_DESTROY_BLOCK) { + this.isDestroyingBlock = false; +- if (!Objects.equals(this.destroyPos, blockpos)) { +- ServerPlayerGameMode.LOGGER.warn("Mismatch in destroy block pos: {} {}", this.destroyPos, blockpos); ++ if (!Objects.equals(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(blockpos, true, j, "aborted mismatched destroying"); ++ this.debugLogging(pos, true, sequence, "aborted mismatched destroying"); + } + +- this.level.destroyBlockProgress(this.player.getId(), blockpos, -1); +- this.debugLogging(blockpos, true, j, "aborted 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 + } + + } + } + +- public void destroyAndAck(BlockPos blockpos, int i, String s) { +- if (this.destroyBlock(blockpos)) { +- this.debugLogging(blockpos, true, i, s); ++ public void destroyAndAck(BlockPos pos, int sequence, String message) { ++ if (this.destroyBlock(pos)) { ++ this.debugLogging(pos, true, sequence, message); + } else { +- this.player.connection.send(new ClientboundBlockUpdatePacket(blockpos, this.level.getBlockState(blockpos))); +- this.debugLogging(blockpos, false, i, s); ++ this.player.connection.send(new ClientboundBlockUpdatePacket(pos, this.level.getBlockState(pos))); ++ this.debugLogging(pos, false, sequence, message); + } + + } + +- public boolean destroyBlock(BlockPos blockpos) { +- BlockState blockstate = this.level.getBlockState(blockpos); ++ public boolean destroyBlock(BlockPos pos) { ++ 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.getMainHandItem().getItem().canAttackBlock(blockstate, this.level, blockpos, this.player)) { ++ 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(blockpos); +- 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(blockpos, blockstate, blockstate, 3); ++ this.level.sendBlockUpdated(pos, iblockdata, iblockdata, 3); + return false; +- } else if (this.player.blockActionRestricted(this.level, blockpos, this.gameModeForPlayer)) { ++ } else if (this.player.blockActionRestricted(this.level, pos, this.gameModeForPlayer)) { + return false; + } else { +- BlockState blockstate1 = block.playerWillDestroy(this.level, blockpos, blockstate, this.player); +- boolean flag = this.level.removeBlock(blockpos, false); ++ // 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, blockpos, blockstate1); ++ block.destroy(this.level, pos, iblockdata1); + } + + if (this.isCreative()) { +- return true; ++ // return true; // CraftBukkit + } else { + ItemStack itemstack = this.player.getMainHandItem(); + ItemStack itemstack1 = itemstack.copy(); +- boolean flag1 = this.player.hasCorrectToolForDrops(blockstate1); ++ boolean flag1 = this.player.hasCorrectToolForDrops(iblockdata1); + +- itemstack.mineBlock(this.level, blockstate1, blockpos, this.player); +- if (flag && flag1) { +- block.playerDestroy(this.level, this.player, blockpos, blockstate1, blockentity, itemstack1); ++ 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 serverplayer, Level level, ItemStack itemstack, InteractionHand interactionhand) { ++ public InteractionResult useItem(ServerPlayer player, Level level, ItemStack stack, EnumHand hand) { + if (this.gameModeForPlayer == GameType.SPECTATOR) { + return InteractionResult.PASS; +- } else if (serverplayer.getCooldowns().isOnCooldown(itemstack.getItem())) { ++ } else if (player.getCooldowns().isOnCooldown(stack.getItem())) { + return InteractionResult.PASS; + } else { +- int i = itemstack.getCount(); +- int j = itemstack.getDamageValue(); +- InteractionResultHolder<ItemStack> interactionresultholder = itemstack.use(level, serverplayer, interactionhand); +- ItemStack itemstack1 = (ItemStack) interactionresultholder.getObject(); ++ int i = stack.getCount(); ++ int j = stack.getDamageValue(); ++ InteractionResultHolder<ItemStack> interactionresultwrapper = stack.use(level, player, hand); ++ ItemStack itemstack1 = (ItemStack) interactionresultwrapper.getObject(); + +- if (itemstack1 == itemstack && itemstack1.getCount() == i && itemstack1.getUseDuration() <= 0 && itemstack1.getDamageValue() == j) { +- return interactionresultholder.getResult(); +- } else if (interactionresultholder.getResult() == InteractionResult.FAIL && itemstack1.getUseDuration() > 0 && !serverplayer.isUsingItem()) { +- return interactionresultholder.getResult(); ++ 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 (itemstack != itemstack1) { +- serverplayer.setItemInHand(interactionhand, itemstack1); ++ if (stack != itemstack1) { ++ player.setItemInHand(hand, itemstack1); + } + + if (this.isCreative() && itemstack1 != ItemStack.EMPTY) { +@@ -301,72 +456,112 @@ + } + + if (itemstack1.isEmpty()) { +- serverplayer.setItemInHand(interactionhand, ItemStack.EMPTY); ++ player.setItemInHand(hand, ItemStack.EMPTY); + } + +- if (!serverplayer.isUsingItem()) { +- serverplayer.inventoryMenu.sendAllDataToRemote(); ++ if (!player.isUsingItem()) { ++ player.inventoryMenu.sendAllDataToRemote(); + } + +- return interactionresultholder.getResult(); ++ return interactionresultwrapper.getResult(); + } + } + } + +- public InteractionResult useItemOn(ServerPlayer serverplayer, Level level, ItemStack itemstack, InteractionHand interactionhand, BlockHitResult blockhitresult) { +- BlockPos blockpos = blockhitresult.getBlockPos(); +- BlockState blockstate = level.getBlockState(blockpos); ++ // 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 (!blockstate.getBlock().isEnabled(level.enabledFeatures())) { ++ if (!iblockdata.getBlock().isEnabled(level.enabledFeatures())) { + return InteractionResult.FAIL; + } else if (this.gameModeForPlayer == GameType.SPECTATOR) { +- MenuProvider menuprovider = blockstate.getMenuProvider(level, blockpos); ++ ITileInventory itileinventory = iblockdata.getMenuProvider(level, blockposition); ++ cancelledBlock = !(itileinventory instanceof ITileInventory); ++ } + +- if (menuprovider != null) { +- serverplayer.openMenu(menuprovider); ++ 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; + } + } else { +- boolean flag = !serverplayer.getMainHandItem().isEmpty() || !serverplayer.getOffhandItem().isEmpty(); +- boolean flag1 = serverplayer.isSecondaryUseActive() && flag; +- ItemStack itemstack1 = itemstack.copy(); ++ boolean flag = !player.getMainHandItem().isEmpty() || !player.getOffhandItem().isEmpty(); ++ boolean flag1 = player.isSecondaryUseActive() && flag; ++ ItemStack itemstack1 = stack.copy(); + + if (!flag1) { +- InteractionResult interactionresult = blockstate.use(level, serverplayer, interactionhand, blockhitresult); ++ enuminteractionresult = iblockdata.use(level, player, hand, hitResult); + +- if (interactionresult.consumesAction()) { +- CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger(serverplayer, blockpos, itemstack1); +- return interactionresult; ++ if (enuminteractionresult.consumesAction()) { ++ CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger(player, blockposition, itemstack1); ++ return enuminteractionresult; + } + } + +- if (!itemstack.isEmpty() && !serverplayer.getCooldowns().isOnCooldown(itemstack.getItem())) { +- UseOnContext useoncontext = new UseOnContext(serverplayer, interactionhand, blockhitresult); +- 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 i = itemstack.getCount(); ++ int i = stack.getCount(); + +- interactionresult1 = itemstack.useOn(useoncontext); +- itemstack.setCount(i); ++ enuminteractionresult1 = stack.useOn(itemactioncontext); ++ stack.setCount(i); + } else { +- interactionresult1 = itemstack.useOn(useoncontext); ++ enuminteractionresult1 = stack.useOn(itemactioncontext); + } + +- if (interactionresult1.consumesAction()) { +- CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger(serverplayer, blockpos, itemstack1); ++ 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) { +- this.level = serverlevel; ++ public void setLevel(ServerLevel serverLevel) { ++ this.level = serverLevel; + } + } |