From 893616e85198e7e55e44a841e9a5bb24dbd4b40b Mon Sep 17 00:00:00 2001 From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com> Date: Tue, 27 Jun 2023 21:09:11 -0400 Subject: [PATCH] Dont resend blocks on interactions In general, the client now has an acknowledgment system which will prevent block changes made by the client to be reverted correctly. It should be noted that this system does not yet support block entities, so those still need to resynced when needed. --- .../level/ServerPlayerGameMode.java.patch | 76 ++++++++++--------- .../world/item/BucketItem.java.patch | 4 +- .../minecraft/world/item/ItemStack.java.patch | 72 +++++++++--------- 3 files changed, 81 insertions(+), 71 deletions(-) diff --git a/paper-server/patches/sources/net/minecraft/server/level/ServerPlayerGameMode.java.patch b/paper-server/patches/sources/net/minecraft/server/level/ServerPlayerGameMode.java.patch index e33c591e54..2cf94caf16 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/ServerPlayerGameMode.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/ServerPlayerGameMode.java.patch @@ -135,20 +135,20 @@ + // Update any tile entity data for this block + capturedBlockEntity = true; // Paper - Send block entities after destroy prediction + // CraftBukkit end - return; - } - ++ return; ++ } ++ + // CraftBukkit start + PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_BLOCK, pos, direction, 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, pos)); ++ // this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos)); // Paper - Don't resync blocks + // Update any tile entity data for this block + capturedBlockEntity = true; // Paper - Send block entities after destroy prediction -+ return; -+ } + return; + } + // CraftBukkit end -+ + if (this.isCreative()) { this.destroyAndAck(pos, sequence, "creative destroy"); return; @@ -157,7 +157,7 @@ + // Spigot start - handle debug stick left click for non-creative + if (this.player.getMainHandItem().is(net.minecraft.world.item.Items.DEBUG_STICK) + && ((net.minecraft.world.item.DebugStickItem) net.minecraft.world.item.Items.DEBUG_STICK).handleInteraction(this.player, this.level.getBlockState(pos), this.level, pos, false, this.player.getMainHandItem())) { -+ this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos)); ++ // this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos)); // Paper - Don't resync block + return; + } + // Spigot end @@ -165,7 +165,7 @@ 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"); -@@ -166,7 +232,19 @@ +@@ -166,7 +232,21 @@ float f = 1.0F; iblockdata = this.level.getBlockState(pos); @@ -173,27 +173,29 @@ + // 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(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) == DoubleBlockHalf.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)); -+ } ++ // Paper start - Don't resync blocks ++ //BlockState 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) == DoubleBlockHalf.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)); ++ //} ++ // Paper end - Don't resync blocks + } else if (!iblockdata.isAir()) { EnchantmentHelper.onHitBlock(this.level, this.player.getMainHandItem(), this.player, this.player, EquipmentSlot.MAINHAND, Vec3.atCenterOf(pos), iblockdata, (item) -> { this.player.onEquippedItemBroken(item, EquipmentSlot.MAINHAND); }); -@@ -174,6 +252,26 @@ +@@ -174,6 +254,26 @@ f = iblockdata.getDestroyProgress(this.player, this.player.level(), pos); } + 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)); ++ // this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos)); // Paper - Don't resync blocks + } + return; + } @@ -201,7 +203,7 @@ + + if (blockEvent.isCancelled()) { + // Let the client know the block still exists -+ this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos)); ++ // this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos)); // Paper - Don't resync block + return; + } + @@ -213,7 +215,7 @@ if (!iblockdata.isAir() && f >= 1.0F) { this.destroyAndAck(pos, sequence, "insta mine"); } else { -@@ -217,14 +315,18 @@ +@@ -217,14 +317,18 @@ this.debugLogging(pos, true, sequence, "stopped destroying"); } else if (action == ServerboundPlayerActionPacket.Action.ABORT_DESTROY_BLOCK) { this.isDestroyingBlock = false; @@ -236,7 +238,7 @@ } } -@@ -242,19 +344,80 @@ +@@ -242,19 +346,82 @@ public boolean destroyBlock(BlockPos pos) { BlockState iblockdata = this.level.getBlockState(pos); @@ -251,7 +253,7 @@ + + // 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 (this.level.getBlockEntity(pos) == null && !isSwordNoBreak) { ++ if (false && this.level.getBlockEntity(pos) == null && !isSwordNoBreak) { // Paper - Don't resync block + ClientboundBlockUpdatePacket packet = new ClientboundBlockUpdatePacket(pos, Blocks.AIR.defaultBlockState()); + this.player.connection.send(packet); + } @@ -277,13 +279,15 @@ + if (isSwordNoBreak) { + return false; + } ++ // Paper start - Don't resync blocks + // Let the client know the block still exists -+ this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos)); ++ //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(this.level, pos.relative(dir))); -+ } ++ //for (Direction dir : Direction.values()) { ++ // this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos.relative(dir))); ++ //} ++ // Paper end - Don't resync blocks + + // Update any tile entity data for this block + if (!captureSentBlockEntities) { // Paper - Send block entities after destroy prediction @@ -319,7 +323,7 @@ BlockState iblockdata1 = block.playerWillDestroy(this.level, pos, iblockdata, this.player); boolean flag = this.level.removeBlock(pos, false); -@@ -262,20 +425,46 @@ +@@ -262,20 +429,46 @@ block.destroy(this.level, pos, iblockdata1); } @@ -370,7 +374,7 @@ } } } -@@ -321,15 +510,59 @@ +@@ -321,15 +514,61 @@ } } @@ -407,16 +411,18 @@ + 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) == DoubleBlockHalf.LOWER; -+ player.connection.send(new ClientboundBlockUpdatePacket(world, bottom ? blockposition.above() : blockposition.below())); ++ // Paper start - Don't resync blocks ++ // boolean bottom = iblockdata.getValue(DoorBlock.HALF) == DoubleBlockHalf.LOWER; ++ // player.connection.send(new ClientboundBlockUpdatePacket(world, bottom ? blockposition.above() : blockposition.below())); ++ // Paper end - Don't resync blocks + } else if (iblockdata.getBlock() instanceof CakeBlock) { + player.getBukkitEntity().sendHealthUpdate(); // SPIGOT-1341 - reset health for cake + } else if (this.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(world, blockposition.relative(hitResult.getDirection()).above())); ++ //player.connection.send(new ClientboundBlockUpdatePacket(world, blockposition.relative(hitResult.getDirection()).above())); // Paper - Don't resync blocks + + // 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(world, blockposition.above())); ++ //player.connection.send(new ClientboundBlockUpdatePacket(world, blockposition.above())); // Paper - Don't resync blocks + // Paper start - extend Player Interact cancellation // TODO: consider merging this into the extracted method + } else if (iblockdata.is(Blocks.JIGSAW) || iblockdata.is(Blocks.STRUCTURE_BLOCK) || iblockdata.getBlock() instanceof net.minecraft.world.level.block.CommandBlock) { + player.connection.send(new net.minecraft.network.protocol.game.ClientboundContainerClosePacket(this.player.containerMenu.containerId)); @@ -430,7 +436,7 @@ if (itileinventory != null) { player.openMenu(itileinventory); return InteractionResult.CONSUME; -@@ -359,7 +592,7 @@ +@@ -359,7 +598,7 @@ } } diff --git a/paper-server/patches/sources/net/minecraft/world/item/BucketItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/BucketItem.java.patch index 5e41944343..6e51878d93 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/BucketItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/BucketItem.java.patch @@ -37,7 +37,7 @@ + PlayerBucketFillEvent event = CraftEventFactory.callPlayerBucketFillEvent((ServerLevel) world, user, blockposition, blockposition, movingobjectpositionblock.getDirection(), itemstack, dummyFluid.getItem(), hand); + if (event.isCancelled()) { -+ ((ServerPlayer) user).connection.send(new ClientboundBlockUpdatePacket(world, blockposition)); // SPIGOT-5163 (see PlayerInteractManager) ++ // ((ServerPlayer) user).connection.send(new ClientboundBlockUpdatePacket(world, blockposition)); // SPIGOT-5163 (see PlayerInteractManager) // Paper - Don't resend blocks + ((ServerPlayer) user).getBukkitEntity().updateInventory(); // SPIGOT-4541 + return InteractionResult.FAIL; + } @@ -117,7 +117,7 @@ + if (flag2 && entityhuman != null) { + PlayerBucketEmptyEvent event = CraftEventFactory.callPlayerBucketEmptyEvent((ServerLevel) world, entityhuman, blockposition, clicked, enumdirection, itemstack, enumhand); + if (event.isCancelled()) { -+ ((ServerPlayer) entityhuman).connection.send(new ClientboundBlockUpdatePacket(world, blockposition)); // SPIGOT-4238: needed when looking through entity ++ // ((ServerPlayer) entityhuman).connection.send(new ClientboundBlockUpdatePacket(world, blockposition)); // SPIGOT-4238: needed when looking through entity // Paper - Don't resend blocks + ((ServerPlayer) entityhuman).getBukkitEntity().updateInventory(); // SPIGOT-4541 + return false; + } diff --git a/paper-server/patches/sources/net/minecraft/world/item/ItemStack.java.patch b/paper-server/patches/sources/net/minecraft/world/item/ItemStack.java.patch index 51b0acc80f..21e20bc8ea 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/ItemStack.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/ItemStack.java.patch @@ -152,7 +152,7 @@ }); } } -@@ -370,32 +418,196 @@ +@@ -370,32 +418,198 @@ } public InteractionResult useOn(UseOnContext context) { @@ -169,7 +169,7 @@ + DataComponentPatch oldData = this.components.asPatch(); + int oldCount = this.getCount(); + ServerLevel world = (ServerLevel) context.getLevel(); -+ + + if (!(item instanceof BucketItem/* || item instanceof SolidBucketItem*/)) { // if not bucket // Paper - Fix cancelled powdered snow bucket placement + world.captureBlockStates = true; + // special case bonemeal @@ -200,7 +200,7 @@ + structureEvent = new StructureGrowEvent(location, treeType, isBonemeal, (Player) entityhuman.getBukkitEntity(), (List< BlockState>) (List) blocks); + org.bukkit.Bukkit.getPluginManager().callEvent(structureEvent); + } - ++ + BlockFertilizeEvent fertilizeEvent = new BlockFertilizeEvent(CraftBlock.at(world, blockposition), (Player) entityhuman.getBukkitEntity(), (List< BlockState>) (List) blocks); + fertilizeEvent.setCancelled(structureEvent != null && structureEvent.isCancelled()); + org.bukkit.Bukkit.getPluginManager().callEvent(fertilizeEvent); @@ -252,10 +252,12 @@ + world.preventPoiUpdated = false; + + // Brute force all possible updates -+ BlockPos placedPos = ((CraftBlock) placeEvent.getBlock()).getPosition(); -+ for (Direction dir : Direction.values()) { -+ ((ServerPlayer) entityhuman).connection.send(new ClientboundBlockUpdatePacket(world, placedPos.relative(dir))); -+ } ++ // Paper start - Don't resync blocks ++ // BlockPos placedPos = ((CraftBlock) placeEvent.getBlock()).getPosition(); ++ // for (Direction dir : Direction.values()) { ++ // ((ServerPlayer) entityhuman).connection.send(new ClientboundBlockUpdatePacket(world, placedPos.relative(dir))); ++ // } ++ // Paper end - Don't resync blocks + SignItem.openSign = null; // SPIGOT-6758 - Reset on early return + } else { + // Change the stack to its new contents if it hasn't been tampered with. @@ -354,7 +356,7 @@ ItemStack itemstack = this.copy(); boolean flag = this.getUseDuration(user) <= 0; InteractionResult enuminteractionresult = this.getItem().use(world, user, hand); -@@ -490,8 +702,37 @@ +@@ -490,27 +704,66 @@ return this.isDamageableItem() && this.getDamageValue() >= this.getMaxDamage() - 1; } @@ -372,7 +374,7 @@ + if (player instanceof final ServerPlayer serverPlayer) { // Paper - Add EntityDamageItemEvent + PlayerItemDamageEvent event = new PlayerItemDamageEvent(serverPlayer.getBukkitEntity(), CraftItemStack.asCraftMirror(this), j, originalDamage); // Paper - Add EntityDamageItemEvent + event.getPlayer().getServer().getPluginManager().callEvent(event); -+ + + if (j != event.getDamage() || event.isCancelled()) { + event.getPlayer().updateInventory(); + } @@ -391,10 +393,10 @@ + // Paper end - Add EntityDamageItemEvent + } + // CraftBukkit end - ++ if (j != 0) { this.applyDamage(this.getDamageValue() + j, player, breakCallback); -@@ -499,18 +740,28 @@ + } } @@ -428,7 +430,7 @@ this.shrink(1); breakCallback.accept(item); -@@ -518,7 +769,7 @@ +@@ -518,7 +771,7 @@ } @@ -437,7 +439,7 @@ if (player instanceof ServerPlayer entityplayer) { int j = this.processDurabilityChange(amount, entityplayer.serverLevel(), entityplayer); -@@ -535,6 +786,11 @@ +@@ -535,6 +788,11 @@ } public void hurtAndBreak(int amount, LivingEntity entity, EquipmentSlot slot) { @@ -449,7 +451,7 @@ Level world = entity.level(); if (world instanceof ServerLevel worldserver) { -@@ -546,9 +802,9 @@ +@@ -546,9 +804,9 @@ entityplayer = null; } @@ -462,7 +464,7 @@ } } -@@ -580,11 +836,11 @@ +@@ -580,11 +838,11 @@ return this.getItem().getBarColor(this); } @@ -476,7 +478,7 @@ return this.getItem().overrideOtherStackedOnMe(this, stack, slot, clickType, player, cursorStackReference); } -@@ -592,8 +848,8 @@ +@@ -592,8 +850,8 @@ Item item = this.getItem(); if (item.hurtEnemy(this, target, user)) { @@ -487,7 +489,7 @@ entityhuman.awardStat(Stats.ITEM_USED.get(item)); } -@@ -608,7 +864,7 @@ +@@ -608,7 +866,7 @@ this.getItem().postHurtEnemy(this, target, user); } @@ -496,7 +498,7 @@ Item item = this.getItem(); if (item.mineBlock(this, world, state, pos, miner)) { -@@ -617,11 +873,11 @@ +@@ -617,11 +875,11 @@ } @@ -510,7 +512,7 @@ return this.getItem().interactLivingEntity(this, user, entity, hand); } -@@ -736,7 +992,7 @@ +@@ -736,7 +994,7 @@ } @@ -519,7 +521,7 @@ player.awardStat(Stats.ITEM_CRAFTED.get(this.getItem()), amount); this.getItem().onCraftedBy(this, world, player); } -@@ -770,6 +1026,12 @@ +@@ -770,6 +1028,12 @@ return this.getItem().useOnRelease(this); } @@ -532,10 +534,13 @@ @Nullable public T set(DataComponentType type, @Nullable T value) { return this.components.set(type, value); -@@ -806,6 +1068,25 @@ - } - } - +@@ -803,8 +1067,27 @@ + this.components.restorePatch(datacomponentpatch1); + } else { + this.getItem().verifyComponentsAfterLoad(this); ++ } ++ } ++ + // Paper start - (this is just a good no conflict location) + public org.bukkit.inventory.ItemStack asBukkitMirror() { + return CraftItemStack.asCraftMirror(this); @@ -550,15 +555,14 @@ + public org.bukkit.inventory.ItemStack getBukkitStack() { + if (bukkitStack == null || bukkitStack.handle != this) { + bukkitStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this); -+ } + } + return bukkitStack; -+ } + } + // Paper end -+ + public void applyComponents(DataComponentPatch changes) { this.components.applyPatch(changes); - this.getItem().verifyComponentsAfterLoad(this); -@@ -858,7 +1139,7 @@ +@@ -858,7 +1141,7 @@ } private void addToTooltip(DataComponentType componentType, Item.TooltipContext context, Consumer textConsumer, TooltipFlag type) { @@ -567,7 +571,7 @@ if (t0 != null) { t0.addToTooltip(context, textConsumer, type); -@@ -866,7 +1147,7 @@ +@@ -866,7 +1149,7 @@ } @@ -576,7 +580,7 @@ boolean flag = this.getItem().shouldPrintOpWarning(this, player); if (!type.isCreative() && this.has(DataComponents.HIDE_TOOLTIP)) { -@@ -941,7 +1222,7 @@ +@@ -941,7 +1224,7 @@ } } @@ -585,7 +589,7 @@ ItemAttributeModifiers itemattributemodifiers = (ItemAttributeModifiers) this.getOrDefault(DataComponents.ATTRIBUTE_MODIFIERS, ItemAttributeModifiers.EMPTY); if (itemattributemodifiers.showInTooltip()) { -@@ -966,7 +1247,7 @@ +@@ -966,7 +1249,7 @@ } } @@ -594,7 +598,7 @@ double d0 = modifier.amount(); boolean flag = false; -@@ -1091,6 +1372,14 @@ +@@ -1091,6 +1374,14 @@ EnchantmentHelper.forEachModifier(this, slot, attributeModifierConsumer); } @@ -609,7 +613,7 @@ public Component getDisplayName() { MutableComponent ichatmutablecomponent = Component.empty().append(this.getHoverName()); -@@ -1153,7 +1442,7 @@ +@@ -1153,7 +1444,7 @@ } public void consume(int amount, @Nullable LivingEntity entity) {