diff --git a/banner-server/src/main/java/com/mohistmc/banner/mixin/server/level/MixinServerPlayerGameMode.java b/banner-server/src/main/java/com/mohistmc/banner/mixin/server/level/MixinServerPlayerGameMode.java index bbfa74f05..e3ad181f2 100644 --- a/banner-server/src/main/java/com/mohistmc/banner/mixin/server/level/MixinServerPlayerGameMode.java +++ b/banner-server/src/main/java/com/mohistmc/banner/mixin/server/level/MixinServerPlayerGameMode.java @@ -6,7 +6,9 @@ import java.util.ArrayList; import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; -import net.minecraft.advancements.CriteriaTriggers; + +import io.izzel.arclight.mixin.Decorate; +import io.izzel.arclight.mixin.DecorationOps; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.network.protocol.Packet; @@ -16,6 +18,7 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayerGameMode; +import net.minecraft.server.network.ServerGamePacketListenerImpl; import net.minecraft.server.players.PlayerList; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; @@ -25,6 +28,7 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.DoubleHighBlockItem; import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemCooldowns; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.GameType; @@ -45,8 +49,10 @@ import org.bukkit.event.Event; import org.bukkit.event.block.Action; import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockDamageEvent; import org.bukkit.event.player.PlayerGameModeChangeEvent; import org.bukkit.event.player.PlayerInteractEvent; +import org.objectweb.asm.Opcodes; import org.slf4j.Logger; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -55,6 +61,8 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(ServerPlayerGameMode.class) @@ -114,155 +122,77 @@ public abstract class MixinServerPlayerGameMode implements InjectionServerPlayer this.player.server.getPlayerList().broadcastAll(new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE, this.player), this.player); } - /** - * @author wdog5 - * @reason functionally replaced - */ - @Overwrite - public void handleBlockBreakAction(BlockPos blockposition, ServerboundPlayerActionPacket.Action packetplayinblockdig_enumplayerdigtype, Direction enumdirection, int i, int j) { - if (!this.player.canInteractWithBlock(blockposition, 1.0)) { - this.debugLogging(blockposition, false, j, "too far"); - } else if (blockposition.getY() >= i) { - this.player.connection.send(new ClientboundBlockUpdatePacket(blockposition, this.level.getBlockState(blockposition))); - this.debugLogging(blockposition, false, j, "too high"); - } else { - if (packetplayinblockdig_enumplayerdigtype == ServerboundPlayerActionPacket.Action.START_DESTROY_BLOCK) { - if (!this.level.mayInteract(this.player, blockposition)) { - // CraftBukkit start - fire PlayerInteractEvent - CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_BLOCK, blockposition, enumdirection, this.player.getInventory().getSelected(), InteractionHand.MAIN_HAND); - this.player.connection.send(new ClientboundBlockUpdatePacket(blockposition, this.level.getBlockState(blockposition))); - this.debugLogging(blockposition, false, j, "may not interact"); - // Update any tile entity data for this block - BlockEntity tileentity = level.getBlockEntity(blockposition); - if (tileentity != null) { - this.player.connection.send(tileentity.getUpdatePacket()); - } - // CraftBukkit end - return; - } - - // CraftBukkit start - PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_BLOCK, blockposition, enumdirection, this.player.getInventory().getSelected(), InteractionHand.MAIN_HAND); - if (event.isCancelled()) { - // Let the client know the block still exists - this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, blockposition)); - // Update any tile entity data for this block - BlockEntity tileentity = this.level.getBlockEntity(blockposition); - if (tileentity != null) { - this.player.connection.send(tileentity.getUpdatePacket()); - } - return; - } - // CraftBukkit end - - if (this.isCreative()) { - this.destroyAndAck(blockposition, j, "creative destroy"); - return; - } - - if (this.player.blockActionRestricted(this.level, blockposition, this.gameModeForPlayer)) { - this.player.connection.send(new ClientboundBlockUpdatePacket(blockposition, this.level.getBlockState(blockposition))); - this.debugLogging(blockposition, false, j, "block action restricted"); - return; - } - - this.destroyProgressStart = this.gameTicks; - float f = 1.0F; - - BlockState iblockdata = this.level.getBlockState(blockposition); - // 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. - BlockState data = this.level.getBlockState(blockposition); - if (data.getBlock() instanceof DoorBlock) { - // For some reason *BOTH* the bottom/top part have to be marked updated. - boolean bottom = data.getValue(DoorBlock.HALF) == DoubleBlockHalf.LOWER; - this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, blockposition)); - this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, bottom ? blockposition.above() : blockposition.below())); - } else if (data.getBlock() instanceof TrapDoorBlock) { - this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, blockposition)); - } - } else if (!iblockdata.isAir()) { - iblockdata.attack(this.level, blockposition, this.player); - f = iblockdata.getDestroyProgress(this.player, this.player.level(), blockposition); - } - - 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, blockposition)); - } - return; - } - org.bukkit.event.block.BlockDamageEvent blockEvent = CraftEventFactory.callBlockDamageEvent(this.player, blockposition, 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, blockposition)); - return; - } - - if (blockEvent.getInstaBreak()) { - f = 2.0f; - } - // CraftBukkit end - - if (!iblockdata.isAir() && f >= 1.0F) { - this.destroyAndAck(blockposition, j, "insta mine"); - } else { - if (this.isDestroyingBlock) { - this.player.connection.send(new ClientboundBlockUpdatePacket(this.destroyPos, this.level.getBlockState(this.destroyPos))); - this.debugLogging(blockposition, false, j, "abort destroying since another started (client insta mine, server disagreed)"); - } - - this.isDestroyingBlock = true; - this.destroyPos = blockposition.immutable(); - int k = (int) (f * 10.0F); - - this.level.destroyBlockProgress(this.player.getId(), blockposition, k); - this.debugLogging(blockposition, true, j, "actual start of destroying"); - this.lastSentState = k; - } - } else if (packetplayinblockdig_enumplayerdigtype == ServerboundPlayerActionPacket.Action.STOP_DESTROY_BLOCK) { - if (blockposition.equals(this.destroyPos)) { - int l = this.gameTicks - this.destroyProgressStart; - - BlockState iblockdata = this.level.getBlockState(blockposition); - if (!iblockdata.isAir()) { - float f1 = iblockdata.getDestroyProgress(this.player, this.player.level(), blockposition) * (float) (l + 1); - - if (f1 >= 0.7F) { - this.isDestroyingBlock = false; - this.level.destroyBlockProgress(this.player.getId(), blockposition, -1); - this.destroyAndAck(blockposition, j, "destroyed"); - return; - } - - if (!this.hasDelayedDestroy) { - this.isDestroyingBlock = false; - this.hasDelayedDestroy = true; - this.delayedDestroyPos = blockposition; - this.delayedTickStart = this.destroyProgressStart; - } - } - } - - this.debugLogging(blockposition, true, j, "stopped destroying"); - } else if (packetplayinblockdig_enumplayerdigtype == ServerboundPlayerActionPacket.Action.ABORT_DESTROY_BLOCK) { - this.isDestroyingBlock = false; - if (!Objects.equals(this.destroyPos, blockposition)) { - LOGGER.debug("Mismatch in destroy block pos: {} {}", this.destroyPos, blockposition); // CraftBukkit - SPIGOT-5457 sent by client when interact event cancelled - this.level.destroyBlockProgress(this.player.getId(), this.destroyPos, -1); - this.debugLogging(blockposition, true, j, "aborted mismatched destroying"); - } + @Redirect(method = "handleBlockBreakAction", at = @At(value = "INVOKE", ordinal = 0, target = "Lnet/minecraft/server/network/ServerGamePacketListenerImpl;send(Lnet/minecraft/network/protocol/Packet;)V"), + slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerLevel;mayInteract(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/core/BlockPos;)Z"))) + private void banner$mayNotInteractEvent(ServerGamePacketListenerImpl instance, Packet packet, BlockPos blockPos, ServerboundPlayerActionPacket.Action action, Direction direction) throws Throwable { + CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_BLOCK, blockPos, direction, this.player.getInventory().getSelected(), InteractionHand.MAIN_HAND); + DecorationOps.callsite().invoke(instance, packet); + BlockEntity blockEntity = this.level.getBlockEntity(blockPos); + if (blockEntity != null) { + this.player.connection.send(blockEntity.getUpdatePacket()); + } + } - this.level.destroyBlockProgress(this.player.getId(), blockposition, -1); - this.debugLogging(blockposition, true, j, "aborted destroying"); + @Decorate(method = "handleBlockBreakAction", inject = true, at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayerGameMode;isCreative()Z")) + private void banner$interactEvent(BlockPos blockPos, ServerboundPlayerActionPacket.Action action, Direction direction, + @io.izzel.arclight.mixin.Local(allocate = "playerInteractEvent") PlayerInteractEvent event) throws Throwable { + event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_BLOCK, blockPos, direction, this.player.getInventory().getSelected(), InteractionHand.MAIN_HAND); + if (event.isCancelled()) { + this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, blockPos)); + BlockEntity blockEntity = this.level.getBlockEntity(blockPos); + if (blockEntity != null) { + this.player.connection.send(blockEntity.getUpdatePacket()); + } + DecorationOps.cancel().invoke(); + return; + } + DecorationOps.blackhole().invoke(); + } - CraftEventFactory.callBlockDamageAbortEvent(this.player, blockposition, this.player.getInventory().getSelected()); // CraftBukkit + @Decorate(method = "handleBlockBreakAction", at = @At(value = "INVOKE", ordinal = 0, target = "Lnet/minecraft/world/level/block/state/BlockState;isAir()Z")) + private boolean banner$playerInteractCancelled(BlockState instance, BlockPos blockPos, ServerboundPlayerActionPacket.Action action, Direction direction, + @io.izzel.arclight.mixin.Local(allocate = "playerInteractEvent") PlayerInteractEvent event) throws Throwable { + boolean result = false; + if (event.useInteractedBlock() == org.bukkit.event.Event.Result.DENY) { + BlockState data = this.level.getBlockState(blockPos); + if (data.getBlock() instanceof DoorBlock) { + boolean bottom = data.getValue(DoorBlock.HALF) == DoubleBlockHalf.LOWER; + this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, blockPos)); + this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, bottom ? blockPos.above() : blockPos.below())); + } else if (data.getBlock() instanceof TrapDoorBlock) { + this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, blockPos)); } + result = true; + } else { + result = (boolean) DecorationOps.callsite().invoke(instance); + } + return result; + } + @Decorate(method = "handleBlockBreakAction", inject = true, at = @At(value = "INVOKE", ordinal = 1, target = "Lnet/minecraft/world/level/block/state/BlockState;isAir()Z")) + private void banner$blockDamageEvent(BlockPos blockPos, ServerboundPlayerActionPacket.Action action, Direction direction, + @io.izzel.arclight.mixin.Local(ordinal = -1) float f, + @io.izzel.arclight.mixin.Local(allocate = "playerInteractEvent") PlayerInteractEvent event) throws Throwable { + if (event.useItemInHand() == Event.Result.DENY) { + if (f > 1.0f) { + this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, blockPos)); + } + return; } + BlockDamageEvent blockEvent = CraftEventFactory.callBlockDamageEvent(this.player, blockPos, this.player.getInventory().getSelected(), f >= 1.0f); + if (blockEvent.isCancelled()) { + this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, blockPos)); + return; + } + if (blockEvent.getInstaBreak()) { + f = 2.0f; + } + DecorationOps.blackhole().invoke(f); + } + + @Inject(method = "handleBlockBreakAction", at = @At(value = "CONSTANT", args = "stringValue=aborted destroying")) + private void banner$abortBlockBreak(BlockPos blockPos, ServerboundPlayerActionPacket.Action action, Direction direction, int i, int j, CallbackInfo ci) { + CraftEventFactory.callBlockDamageAbortEvent(this.player, blockPos, this.player.getInventory().getSelected()); } private final AtomicReference banner$event = new AtomicReference<>(); @@ -373,96 +303,46 @@ public void handleBlockBreakAction(BlockPos blockposition, ServerboundPlayerActi public InteractionHand interactHand; public ItemStack interactItemStack; - /** - * @author wdog4 - * @reason - */ - @Overwrite - public InteractionResult useItemOn(ServerPlayer player, Level level, ItemStack stack, InteractionHand hand, BlockHitResult hitResult) { - BlockPos blockPos = hitResult.getBlockPos(); - BlockState blockState = level.getBlockState(blockPos); - InteractionResult enuminteractionresult = InteractionResult.PASS; + @Inject(method = "useItemOn", cancellable = true, at = @At(value = "FIELD", opcode = Opcodes.GETFIELD, ordinal = 0, target = "Lnet/minecraft/server/level/ServerPlayerGameMode;gameModeForPlayer:Lnet/minecraft/world/level/GameType;")) + private void banner$rightClickBlock(ServerPlayer playerIn, Level worldIn, ItemStack stackIn, InteractionHand handIn, BlockHitResult blockRaytraceResultIn, CallbackInfoReturnable cir) { + BlockPos blockpos = blockRaytraceResultIn.getBlockPos(); + BlockState blockstate = worldIn.getBlockState(blockpos); boolean cancelledBlock = false; - if (!blockState.getBlock().isEnabled(level.enabledFeatures())) { - return InteractionResult.FAIL; - } else if (this.gameModeForPlayer == GameType.SPECTATOR) { - MenuProvider menuProvider = blockState.getMenuProvider(level, blockPos); - cancelledBlock = !(menuProvider instanceof MenuProvider); + if (this.gameModeForPlayer == GameType.SPECTATOR) { + MenuProvider provider = blockstate.getMenuProvider(worldIn, blockpos); + cancelledBlock = !(provider instanceof MenuProvider); } - if (player.getCooldowns().isOnCooldown(stack.getItem())) { + if (playerIn.getCooldowns().isOnCooldown(stackIn.getItem())) { cancelledBlock = true; } - - PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(player, Action.RIGHT_CLICK_BLOCK, blockPos, hitResult.getDirection(), stack, cancelledBlock, hand, hitResult.getLocation()); + PlayerInteractEvent bukkitEvent = CraftEventFactory.callPlayerInteractEvent(playerIn, Action.RIGHT_CLICK_BLOCK, blockpos, blockRaytraceResultIn.getDirection(), stackIn, cancelledBlock, handIn, blockRaytraceResultIn.getLocation()); firedInteract = true; - interactResult = event.useItemInHand() == Event.Result.DENY; - interactPosition = blockPos.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 (blockState.getBlock() instanceof DoorBlock) { - boolean bottom = blockState.getValue(DoorBlock.HALF) == DoubleBlockHalf.LOWER; - player.connection.send(new ClientboundBlockUpdatePacket(level, bottom ? blockPos.above() : blockPos.below())); - } else if (blockState.getBlock() instanceof CakeBlock) { - player.getBukkitEntity().sendHealthUpdate(); // SPIGOT-1341 - reset health for cake - } else if (interactItemStack.getItem() instanceof DoubleHighBlockItem) { + interactResult = bukkitEvent.useItemInHand() == Event.Result.DENY; + interactPosition = blockpos.immutable(); + interactHand = handIn; + interactItemStack = stackIn.copy(); + if (bukkitEvent.useInteractedBlock() == Event.Result.DENY) { + if (blockstate.getBlock() instanceof DoorBlock) { + boolean bottom = blockstate.getValue(DoorBlock.HALF) == DoubleBlockHalf.LOWER; + playerIn.connection.send(new ClientboundBlockUpdatePacket(this.level, bottom ? blockpos.above() : blockpos.below())); + } else if (blockstate.getBlock() instanceof CakeBlock) { + ((ServerPlayer) playerIn).getBukkitEntity().sendHealthUpdate(); + } else if (stackIn.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, blockPos.relative(hitResult.getDirection()).above())); - + playerIn.connection.send(new ClientboundBlockUpdatePacket(level, blockpos.relative(blockRaytraceResultIn.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, blockPos.above())); - } - player.getBukkitEntity().updateInventory(); // SPIGOT-2867 - enuminteractionresult = (event.useItemInHand() != Event.Result.ALLOW) ? InteractionResult.SUCCESS : InteractionResult.PASS; - } else if (this.gameModeForPlayer == GameType.SPECTATOR) { - MenuProvider menuProvider = blockState.getMenuProvider(level, blockPos); - if (menuProvider != null) { - player.openMenu(menuProvider); - return InteractionResult.SUCCESS; - } else { - return InteractionResult.PASS; - } - } else { - boolean bl = !player.getMainHandItem().isEmpty() || !player.getOffhandItem().isEmpty(); - boolean bl2 = player.isSecondaryUseActive() && bl; - ItemStack itemStack = stack.copy(); - if (!bl2) { - ItemInteractionResult result = blockState.useItemOn(player.getItemInHand(hand), level, player, hand, hitResult); - if (result.consumesAction()) { - CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger(player, blockPos, itemStack); - return result.result(); - } - - if (result == ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION && hand == InteractionHand.MAIN_HAND) { - enuminteractionresult = blockState.useWithoutItem(level, player, hitResult); - if (enuminteractionresult.consumesAction()) { - CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger(player, blockPos, itemStack); - return enuminteractionresult; - } - } - } - - if (!stack.isEmpty() && enuminteractionresult != InteractionResult.SUCCESS && !interactResult) { // add !interactResult SPIGOT-764 - UseOnContext useOnContext = new UseOnContext(player, hand, hitResult); - InteractionResult interactionResult2; - if (this.isCreative()) { - int i = stack.getCount(); - interactionResult2 = stack.useOn(useOnContext); - stack.setCount(i); - } else { - interactionResult2 = stack.useOn(useOnContext); - } - - if (interactionResult2.consumesAction()) { - CriteriaTriggers.ITEM_USED_ON_BLOCK.trigger(player, blockPos, itemStack); - } - - return interactionResult2; + playerIn.connection.send(new ClientboundBlockUpdatePacket(level, blockpos.above())); } + ((ServerPlayer) playerIn).getBukkitEntity().updateInventory(); + cir.setReturnValue((bukkitEvent.useItemInHand() != Event.Result.ALLOW) ? InteractionResult.SUCCESS : InteractionResult.PASS); } - return enuminteractionresult; + } + + @Decorate(method = "useItemOn", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemCooldowns;isOnCooldown(Lnet/minecraft/world/item/Item;)Z")) + private boolean banner$useInteractResult(ItemCooldowns instance, Item item) throws Throwable { + var result = (boolean) DecorationOps.callsite().invoke(instance, item); + DecorationOps.blackhole().invoke(result); + return interactResult; } @Override