diff --git a/paper-api/src/main/java/org/bukkit/event/inventory/InventoryDragEvent.java b/paper-api/src/main/java/org/bukkit/event/inventory/InventoryDragEvent.java index a10d5da13d..5f541524f1 100644 --- a/paper-api/src/main/java/org/bukkit/event/inventory/InventoryDragEvent.java +++ b/paper-api/src/main/java/org/bukkit/event/inventory/InventoryDragEvent.java @@ -143,7 +143,7 @@ public class InventoryDragEvent extends InventoryInteractEvent { */ @NotNull public Set getRawSlots() { - return this.addedItems.keySet(); + return Collections.unmodifiableSet(this.addedItems.keySet()); } /** diff --git a/paper-server/patches/features/0016-Moonrise-optimisation-patches.patch b/paper-server/patches/features/0016-Moonrise-optimisation-patches.patch index 235f2c0160..4be82944c5 100644 --- a/paper-server/patches/features/0016-Moonrise-optimisation-patches.patch +++ b/paper-server/patches/features/0016-Moonrise-optimisation-patches.patch @@ -23867,7 +23867,7 @@ index 46de98a6bbbae48c4837e1e588ba198a363d2dde..fd3553bdc1c3cdbf6aa3dc00e0a4987f thread1 -> { DedicatedServer dedicatedServer1 = new DedicatedServer( diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 03a616fc9b0325aa163fe4950ec4ce9ffdd3a9ea..b6aee251027cf124d6597137abefc7fd177358c9 100644 +index 3527c39f3f95832d52aeda6205bbbb7161ecaf66..dc47259e40089a4793d950d4c3dc5bcc51ff680f 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -173,7 +173,7 @@ import net.minecraft.world.phys.Vec2; @@ -27691,7 +27691,7 @@ index 49008b4cbaead8a66a93d2b0d4b50b335a6c3eed..f9c96bbdc54e68b9216b7f8662bfae03 } diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index 6f7f92cc43c56a7453b289f205502d089474ef6d..b390ba657b8b880e431c84e9dd948ac9c84af2fd 100644 +index 2f3b5164356e3a91e63054faac92981d7197d0fb..18439057b996a55833e1bfc60dc293de432d2902 100644 --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java @@ -193,7 +193,7 @@ import net.minecraft.world.scores.Team; @@ -28728,7 +28728,7 @@ index 8cc5c0716392ba06501542ff5cbe71ee43979e5d..09fd99c9cbd23b5f3c899bfb00c9b896 + // Paper end - block counting } diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index 06d07f93e42edcfdd7d69df0b52efe2a58e7dfc1..da880f52920b1101f23ef94f3fd0dbdea218c373 100644 +index 7ece48c7c2e875eafa5a223b34b99c2ed9f3a8b8..813064b4d135c34cf76437a0f26546a8863abf85 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java @@ -147,7 +147,7 @@ import net.minecraft.world.waypoints.WaypointTransmitter; @@ -29203,7 +29203,7 @@ index 06d07f93e42edcfdd7d69df0b52efe2a58e7dfc1..da880f52920b1101f23ef94f3fd0dbde } public InteractionResult interact(Player player, InteractionHand hand) { -@@ -4291,15 +4528,17 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4290,15 +4527,17 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public Iterable getIndirectPassengers() { @@ -29229,7 +29229,7 @@ index 06d07f93e42edcfdd7d69df0b52efe2a58e7dfc1..da880f52920b1101f23ef94f3fd0dbde } public int countPlayerPassengers() { -@@ -4442,77 +4681,136 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4441,77 +4680,136 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess return Mth.lerp(partialTick, this.yRotO, this.yRot); } @@ -29420,7 +29420,7 @@ index 06d07f93e42edcfdd7d69df0b52efe2a58e7dfc1..da880f52920b1101f23ef94f3fd0dbde public boolean touchingUnloadedChunk() { AABB aabb = this.getBoundingBox().inflate(1.0); -@@ -4667,6 +4965,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4666,6 +4964,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } public final void setPosRaw(double x, double y, double z, boolean forceBoundingBoxUpdate) { @@ -29436,7 +29436,7 @@ index 06d07f93e42edcfdd7d69df0b52efe2a58e7dfc1..da880f52920b1101f23ef94f3fd0dbde if (!checkPosition(this, x, y, z)) { return; } -@@ -4818,6 +5125,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4817,6 +5124,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess @Override public final void setRemoved(Entity.RemovalReason removalReason, @Nullable org.bukkit.event.entity.EntityRemoveEvent.Cause cause) { // CraftBukkit - add Bukkit remove cause @@ -29449,7 +29449,7 @@ index 06d07f93e42edcfdd7d69df0b52efe2a58e7dfc1..da880f52920b1101f23ef94f3fd0dbde org.bukkit.craftbukkit.event.CraftEventFactory.callEntityRemoveEvent(this, cause); // CraftBukkit final boolean alreadyRemoved = this.removalReason != null; // Paper - Folia schedulers if (this.removalReason == null) { -@@ -4828,7 +5141,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4827,7 +5140,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess this.stopRiding(); } @@ -29458,7 +29458,7 @@ index 06d07f93e42edcfdd7d69df0b52efe2a58e7dfc1..da880f52920b1101f23ef94f3fd0dbde this.levelCallback.onRemove(removalReason); this.onRemoval(removalReason); // Paper start - Folia schedulers -@@ -4862,7 +5175,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -4861,7 +5174,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess public boolean shouldBeSaved() { return (this.removalReason == null || this.removalReason.shouldSave()) && !this.isPassenger() diff --git a/paper-server/patches/features/0024-Incremental-chunk-and-player-saving.patch b/paper-server/patches/features/0024-Incremental-chunk-and-player-saving.patch index 2b92a0550b..048f96f69d 100644 --- a/paper-server/patches/features/0024-Incremental-chunk-and-player-saving.patch +++ b/paper-server/patches/features/0024-Incremental-chunk-and-player-saving.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Incremental chunk and player saving diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java -index 338ef549efe82c250c74365c1c1071986920c8c9..39581095ccc69d113d954ed835bdfa32d25b5489 100644 +index dc47259e40089a4793d950d4c3dc5bcc51ff680f..34b10db71d22cb7211dcfc5565e6833c8d4d2413 100644 --- a/net/minecraft/server/MinecraftServer.java +++ b/net/minecraft/server/MinecraftServer.java @@ -955,7 +955,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop com.mojang.datafixers.util.Pair.of(slot, ((LivingEntity) target).getItemBySlot(slot).copy())) + .collect(Collectors.toList()), true)); // Paper - sanitize ++ player.containerMenu.sendAllDataToRemote(); ++ } else { ++ ServerGamePacketListenerImpl.this.player.containerMenu.forceHeldSlot(hand); // Paper - fix slot desync (is this needed?) + } -+ -+ ServerGamePacketListenerImpl.this.player.containerMenu.sendAllDataToRemote(); // Paper - fix slot desync - always refresh player inventory + } + + if (event.isCancelled()) { @@ -1865,12 +1866,6 @@ + // CraftBukkit end + InteractionResult result = entityInteraction.run(ServerGamePacketListenerImpl.this.player, target, hand); + -+ // CraftBukkit start -+ if (!itemInHand.isEmpty() && itemInHand.getCount() <= -1) { -+ ServerGamePacketListenerImpl.this.player.containerMenu.sendAllDataToRemote(); -+ } -+ // CraftBukkit end -+ + if (result instanceof InteractionResult.Success success // CraftBukkit + ) { ItemStack itemStack1 = success.wasItemInteraction() ? itemStack : ItemStack.EMPTY; @@ -1892,7 +1887,7 @@ ); } -@@ -1738,14 +_,19 @@ +@@ -1738,14 +_,14 @@ public void onAttack() { if (!(target instanceof ItemEntity) && !(target instanceof ExperienceOrb) @@ -1902,11 +1897,6 @@ ItemStack itemInHand = ServerGamePacketListenerImpl.this.player.getItemInHand(InteractionHand.MAIN_HAND); if (itemInHand.isItemEnabled(serverLevel.enabledFeatures())) { ServerGamePacketListenerImpl.this.player.attack(target); -+ // CraftBukkit start -+ if (!itemInHand.isEmpty() && itemInHand.getCount() <= -1) { -+ ServerGamePacketListenerImpl.this.player.containerMenu.sendAllDataToRemote(); -+ } -+ // CraftBukkit end } } else { - ServerGamePacketListenerImpl.this.disconnect(Component.translatable("multiplayer.disconnect.invalid_entity_attacked")); @@ -1996,7 +1986,7 @@ this.player.containerMenu.sendAllDataToRemote(); } else if (!this.player.containerMenu.stillValid(this.player)) { LOGGER.debug("Player {} interacted with invalid menu {}", this.player, this.player.containerMenu); -@@ -1811,7 +_,340 @@ +@@ -1811,7 +_,339 @@ } else { boolean flag = packet.stateId() != this.player.containerMenu.getStateId(); this.player.containerMenu.suppressRemoteUpdates(); @@ -2298,7 +2288,6 @@ + case HOTBAR_SWAP: + case COLLECT_TO_CURSOR: + case UNKNOWN: -+ this.player.containerMenu.sendAllDataToRemote(); + break; + // Modified cursor and clicked + case PICKUP_SOME: diff --git a/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch index 6bf39741d4..fca7e19932 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch @@ -1064,7 +1064,7 @@ this.gameEvent(GameEvent.ENTITY_INTERACT, player); this.playSound(SoundEvents.LEAD_UNTIED); -@@ -2133,9 +_,23 @@ +@@ -2133,9 +_,22 @@ if (itemInHand1.is(Items.LEAD) && !(leashable2.getLeashHolder() instanceof Player)) { if (!this.level().isClientSide() && leashable2.canHaveALeashAttachedTo(player)) { if (leashable2.isLeashed()) { @@ -1082,7 +1082,6 @@ + // Paper start - EntityLeashEvent + if (org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerLeashEntityEvent(this, player, player, hand).isCancelled()) { + ((ServerPlayer) player).connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityLinkPacket(this, leashable2.getLeashHolder())); -+ player.containerMenu.sendAllDataToRemote(); // Paper - Fix inventory desync + return InteractionResult.PASS; + } + // Paper end - EntityLeashEvent diff --git a/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch index 3553abdf60..1010a8143b 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch @@ -1703,7 +1703,7 @@ } } } -@@ -3264,12 +_,49 @@ +@@ -3264,7 +_,38 @@ this.releaseUsingItem(); } else { if (!this.useItem.isEmpty() && this.isUsingItem()) { @@ -1719,12 +1719,11 @@ + this.level().getCraftServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { -+ // Update client + net.minecraft.world.item.component.Consumable consumable = this.useItem.get(DataComponents.CONSUMABLE); + if (consumable != null) { + consumable.cancelUsingItem(serverPlayer, this.useItem); + } -+ serverPlayer.containerMenu.sendAllDataToRemote(); ++ serverPlayer.containerMenu.forceHeldSlot(this.getUsedItemHand()); + serverPlayer.getBukkitEntity().updateScaledHealth(); + this.stopUsingItem(); // Paper - event is using an item, clear active item to reset its use + return; @@ -1744,16 +1743,6 @@ if (itemStack != this.useItem) { this.setItemInHand(usedItemHand, itemStack); } - - this.stopUsingItem(); -+ // Paper start -+ if (this instanceof Player player) { -+ player.containerMenu.sendAllDataToRemote(); -+ } -+ // Paper end - } - } - } @@ -3294,6 +_,7 @@ ItemStack itemInHand = this.getItemInHand(this.getUsedItemHand()); if (!this.useItem.isEmpty() && ItemStack.isSameItem(itemInHand, this.useItem)) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/Bucketable.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/Bucketable.java.patch index c28cc0e15c..8578946bba 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/Bucketable.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/Bucketable.java.patch @@ -12,7 +12,7 @@ + org.bukkit.event.player.PlayerBucketEntityEvent playerBucketFishEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerFishBucketEvent(entity, player, itemInHand, bucketItemStack, hand); + bucketItemStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(playerBucketFishEvent.getEntityBucket()); + if (playerBucketFishEvent.isCancelled()) { -+ player.containerMenu.sendAllDataToRemote(); // We need to update inventory to resync client's bucket ++ player.containerMenu.forceHeldSlot(hand); // We need to update inventory to resync client's bucket + entity.resendPossiblyDesyncedEntityData((ServerPlayer) player); // Paper + return Optional.of(InteractionResult.FAIL); + } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/player/Player.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/player/Player.java.patch index e79835e6ac..c139a00ed6 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/player/Player.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/player/Player.java.patch @@ -386,7 +386,7 @@ ); } } -@@ -1264,10 +_,11 @@ +@@ -1264,10 +_,10 @@ } } @@ -396,7 +396,6 @@ - this.level() - .playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_NODAMAGE, this.getSoundSource(), 1.0F, 1.0F); + sendSoundEffect(this, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_NODAMAGE, this.getSoundSource(), 1.0F, 1.0F); // Paper - send while respecting visibility -+ this.containerMenu.sendAllDataToRemote(); // CraftBukkit - resync on cancelled event + // CraftBukkit end } } diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/AbstractContainerMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/AbstractContainerMenu.java.patch index ba70c9588e..b3d50d1ca5 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/AbstractContainerMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/AbstractContainerMenu.java.patch @@ -37,7 +37,7 @@ protected AbstractContainerMenu(@Nullable MenuType menuType, int containerId) { this.menuType = menuType; -@@ -176,8 +_,19 @@ +@@ -176,8 +_,43 @@ if (this.synchronizer != null) { this.synchronizer.sendInitialData(this, list, carried.copy(), this.remoteDataSlots.toIntArray()); @@ -47,6 +47,30 @@ + } + } + ++ // Paper start ++ public void forceHeldSlot(final net.minecraft.world.InteractionHand hand) { ++ this.sendAllDataToRemote(); ++ } ++ ++ public void forceHeldSlotAndArmor(final net.minecraft.world.InteractionHand hand) { ++ this.sendAllDataToRemote(); ++ } ++ ++ public void forceSlot(final Container container, final int slot) { ++ final int slotsIndex = this.findSlot(container, slot).orElse(-1); ++ if (slotsIndex == -1) { ++ return; ++ } ++ ++ final ItemStack item = this.slots.get(slotsIndex).getItem(); ++ this.remoteSlots.get(slotsIndex).force(item); ++ ++ if (this.synchronizer != null) { ++ this.synchronizer.sendSlotChange(this, slotsIndex, item.copy()); ++ } ++ } ++ // Paper end ++ + // CraftBukkit start - from synchronizeCarriedToRemote + public void broadcastCarriedItem() { + ItemStack carried = this.getCarried(); @@ -80,11 +104,11 @@ } int count = this.getCarried().getCount(); -+ java.util.Map draggedSlots = new java.util.HashMap<>(); // CraftBukkit - Store slots from drag in map (raw slot id -> new stack) ++ it.unimi.dsi.fastutil.ints.Int2ObjectMap draggedSlots = new it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<>(); // CraftBukkit - Store slots from drag in map (raw slot id -> new stack) for (Slot slot1 : this.quickcraftSlots) { ItemStack carried1 = this.getCarried(); -@@ -387,12 +_,48 @@ +@@ -387,12 +_,46 @@ int min = Math.min(itemStack.getMaxStackSize(), slot1.getMaxStackSize(itemStack)); int min1 = Math.min(getQuickCraftPlaceCount(this.quickcraftSlots, this.quickcraftType, itemStack) + i2, min); count -= min1 - i2; @@ -101,39 +125,37 @@ + + // CraftBukkit start - InventoryDragEvent + org.bukkit.inventory.InventoryView view = this.getBukkitView(); -+ org.bukkit.inventory.ItemStack newcursor = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack); -+ newcursor.setAmount(count); -+ java.util.Map eventmap = new java.util.HashMap<>(); -+ for (java.util.Map.Entry ditem : draggedSlots.entrySet()) { -+ eventmap.put(ditem.getKey(), org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(ditem.getValue())); ++ org.bukkit.inventory.ItemStack newCarried = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack); ++ newCarried.setAmount(count); ++ java.util.Map eventMap = new java.util.HashMap<>(); ++ for (it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry entry : draggedSlots.int2ObjectEntrySet()) { ++ eventMap.put(entry.getIntKey(), org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(entry.getValue())); + } + + // It's essential that we set the cursor to the new value here to prevent item duplication if a plugin closes the inventory. -+ ItemStack oldCursor = this.getCarried(); -+ this.setCarried(org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(newcursor)); ++ ItemStack oldCarried = this.getCarried(); ++ this.setCarried(org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(newCarried)); + -+ org.bukkit.event.inventory.InventoryDragEvent event = new org.bukkit.event.inventory.InventoryDragEvent(view, (newcursor.getType() != org.bukkit.Material.AIR ? newcursor : null), org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(oldCursor), this.quickcraftType == 1, eventmap); ++ org.bukkit.event.inventory.InventoryDragEvent event = new org.bukkit.event.inventory.InventoryDragEvent( ++ view, ++ newCarried, ++ org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(oldCarried), ++ this.quickcraftType == QUICKCRAFT_TYPE_GREEDY, ++ eventMap ++ ); + event.callEvent(); + -+ // Whether a change was made to the inventory that requires an update. -+ boolean needsUpdate = event.getResult() != org.bukkit.event.Event.Result.DEFAULT; -+ + if (event.getResult() != org.bukkit.event.Event.Result.DENY) { -+ for (java.util.Map.Entry dslot : draggedSlots.entrySet()) { -+ view.setItem(dslot.getKey(), org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(dslot.getValue())); ++ for (final it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry entry : draggedSlots.int2ObjectEntrySet()) { ++ view.setItem(entry.getIntKey(), org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(entry.getValue())); + } -+ // The only time the carried item will be set to null is if the inventory is closed by the server. ++ // The only time the carried item will be set to empty is if the inventory is closed by the server. + // If the inventory is closed by the server, then the cursor items are dropped. This is why we change the cursor early. -+ if (this.getCarried() != null) { ++ if (!this.getCarried().isEmpty()) { + this.setCarried(org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getCursor())); -+ needsUpdate = true; + } + } else { -+ this.setCarried(oldCursor); -+ } -+ -+ if (needsUpdate && player instanceof ServerPlayer) { -+ this.sendAllDataToRemote(); ++ this.setCarried(oldCarried); + } + // CraftBukkit end } diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/InventoryMenu.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/InventoryMenu.java.patch index 2aff25815e..add37f1e16 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/InventoryMenu.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/InventoryMenu.java.patch @@ -15,7 +15,7 @@ this.active = active; this.owner = owner; this.addResultSlot(owner, 154, 28); -@@ -188,4 +_,17 @@ +@@ -188,4 +_,38 @@ protected Player owner() { return this.owner; } @@ -31,5 +31,26 @@ + this.view = new org.bukkit.craftbukkit.inventory.CraftInventoryView(this.owner.getBukkitEntity(), inventory, this); + return this.view; + } ++ ++ @Override ++ public void forceHeldSlot(final net.minecraft.world.InteractionHand hand) { ++ // If ever needed, a config option for instead synchronizing the full inventory can be added here to call this.sendAllDataToRemote(); ++ // Otherwise, only resync the hand slot ++ final int slot = hand == net.minecraft.world.InteractionHand.MAIN_HAND ? this.owner.getInventory().getSelectedSlot() : Inventory.SLOT_OFFHAND; ++ this.forceSlot(this.owner.getInventory(), slot); ++ } ++ ++ @Override ++ public void forceHeldSlotAndArmor(final net.minecraft.world.InteractionHand hand) { ++ this.forceHeldSlot(hand); ++ ++ final int size = net.minecraft.world.entity.player.Inventory.INVENTORY_SIZE; ++ final net.minecraft.world.entity.player.Inventory inventory = this.owner.getInventory(); ++ this.forceSlot(inventory, net.minecraft.world.entity.EquipmentSlot.FEET.getIndex(size)); ++ this.forceSlot(inventory, net.minecraft.world.entity.EquipmentSlot.LEGS.getIndex(size)); ++ this.forceSlot(inventory, net.minecraft.world.entity.EquipmentSlot.CHEST.getIndex(size)); ++ this.forceSlot(inventory, net.minecraft.world.entity.EquipmentSlot.HEAD.getIndex(size)); ++ } ++ + // CraftBukkit end } diff --git a/paper-server/patches/sources/net/minecraft/world/item/BlockItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/BlockItem.java.patch index a529dd9ad7..bed997e8de 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/BlockItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/BlockItem.java.patch @@ -15,7 +15,7 @@ if (placementState == null) { return InteractionResult.FAIL; } else if (!this.placeBlock(blockPlaceContext, placementState)) { -@@ -69,15 +_,40 @@ +@@ -69,15 +_,39 @@ BlockState blockState = level.getBlockState(clickedPos); if (blockState.is(placementState.getBlock())) { blockState = this.updateBlockStateFromTag(clickedPos, level, itemInHand, blockState); @@ -40,8 +40,7 @@ + if (placeEvent != null && (placeEvent.isCancelled() || !placeEvent.canBuild())) { + ((org.bukkit.craftbukkit.block.CraftBlockState) bukkitState).revertPlace(); + -+ // Paper - if the event is called here, the inventory should be updated -+ player.containerMenu.sendAllDataToRemote(); // SPIGOT-4541 ++ player.containerMenu.forceHeldSlot(blockPlaceContext.getHand()); + return InteractionResult.FAIL; + } + } diff --git a/paper-server/patches/sources/net/minecraft/world/item/CrossbowItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/CrossbowItem.java.patch index b6ba21e11a..a68d47e47d 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/CrossbowItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/CrossbowItem.java.patch @@ -39,7 +39,7 @@ if (shooter instanceof ServerPlayer serverPlayer) { CriteriaTriggers.SHOT_CROSSBOW.trigger(serverPlayer, weapon); serverPlayer.awardStat(Stats.ITEM_USED.get(weapon.getItem())); -@@ -211,7 +_,14 @@ +@@ -211,7 +_,13 @@ ); } @@ -48,7 +48,6 @@ + // Paper start - Add EntityLoadCrossbowEvent + final io.papermc.paper.event.entity.EntityLoadCrossbowEvent event = new io.papermc.paper.event.entity.EntityLoadCrossbowEvent(livingEntity.getBukkitLivingEntity(), stack.asBukkitMirror(), org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(livingEntity.getUsedItemHand())); + if (!event.callEvent() || !tryLoadProjectiles(livingEntity, stack, event.shouldConsumeItem()) || !event.shouldConsumeItem()) { -+ if (livingEntity instanceof ServerPlayer player) player.containerMenu.sendAllDataToRemote(); + return; + } + // Paper end - Add EntityLoadCrossbowEvent diff --git a/paper-server/patches/sources/net/minecraft/world/item/EggItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/EggItem.java.patch index 2f1eca1b2a..936bc34483 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/EggItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/EggItem.java.patch @@ -24,7 +24,7 @@ + if (event.shouldConsume()) { + itemInHand.consume(1, player); + } else { -+ player.containerMenu.sendAllDataToRemote(); ++ player.containerMenu.forceHeldSlot(hand); + } + level.playSound( + // Paper end @@ -44,7 +44,7 @@ + player.awardStat(Stats.ITEM_USED.get(this)); + // Paper start + } else { -+ player.containerMenu.sendAllDataToRemote(); ++ player.containerMenu.forceHeldSlot(hand); + return InteractionResult.FAIL; + } + // CraftBukkit end diff --git a/paper-server/patches/sources/net/minecraft/world/item/EnderpearlItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/EnderpearlItem.java.patch index 5b8ef433be..28fbeb9fca 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/EnderpearlItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/EnderpearlItem.java.patch @@ -24,7 +24,7 @@ + if (event.shouldConsume()) { + itemInHand.consume(1, player); + } else { -+ player.containerMenu.sendAllDataToRemote(); ++ player.containerMenu.forceHeldSlot(hand); + } + + level.playSound( @@ -44,7 +44,7 @@ + serverPlayer.connection.send(new net.minecraft.network.protocol.game.ClientboundCooldownPacket(player.getCooldowns().getCooldownGroup(itemInHand), 0)); // prevent visual desync of cooldown on the slot + } + // Paper end - PlayerLaunchProjectileEvent -+ player.containerMenu.sendAllDataToRemote(); ++ player.containerMenu.forceHeldSlot(hand); + return InteractionResult.FAIL; + } } diff --git a/paper-server/patches/sources/net/minecraft/world/item/ExperienceBottleItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/ExperienceBottleItem.java.patch index 9ec3ea678d..dba0713d39 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/ExperienceBottleItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/ExperienceBottleItem.java.patch @@ -24,7 +24,7 @@ + if (event.shouldConsume()) { + itemInHand.consume(1, player); + } else { -+ player.containerMenu.sendAllDataToRemote(); ++ player.containerMenu.forceHeldSlot(hand); + } + + level.playSound( @@ -38,7 +38,7 @@ + 0.4F / (level.getRandom().nextFloat() * 0.4F + 0.8F) + ); + } else { -+ player.containerMenu.sendAllDataToRemote(); ++ player.containerMenu.forceHeldSlot(hand); + return InteractionResult.FAIL; + } + // Paper end - PlayerLaunchProjectileEvent diff --git a/paper-server/patches/sources/net/minecraft/world/item/FireworkRocketItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/FireworkRocketItem.java.patch index 39ba53dc6f..f767729f2d 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/FireworkRocketItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/FireworkRocketItem.java.patch @@ -21,7 +21,7 @@ + com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) context.getPlayer().getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemInHand), (org.bukkit.entity.Firework) fireworkRocketEntity.projectile().getBukkitEntity()); + if (!event.callEvent() || !fireworkRocketEntity.attemptSpawn()) return InteractionResult.PASS; + if (event.shouldConsume() && !context.getPlayer().hasInfiniteMaterials()) itemInHand.shrink(1); -+ else context.getPlayer().containerMenu.sendAllDataToRemote(); ++ else context.getPlayer().containerMenu.forceHeldSlot(context.getHand()); + // Paper end - PlayerLaunchProjectileEvent } @@ -43,10 +43,10 @@ + if (event.shouldConsume() && !player.hasInfiniteMaterials()) { + itemInHand.shrink(1); // Moved up from below + } else { -+ player.containerMenu.sendAllDataToRemote(); ++ player.containerMenu.forceHeldSlot(hand); + } + } else { -+ player.containerMenu.sendAllDataToRemote(); ++ player.containerMenu.forceHeldSlot(hand); } - - Projectile.spawnProjectile(new FireworkRocketEntity(level, itemInHand, player), serverLevel, itemInHand); diff --git a/paper-server/patches/sources/net/minecraft/world/item/HoneycombItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/HoneycombItem.java.patch index b01647c48e..a20189f729 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/HoneycombItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/HoneycombItem.java.patch @@ -7,7 +7,7 @@ + // Paper start - EntityChangeBlockEvent + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, clickedPos, blockState1)) { + if (!player.isCreative()) { -+ player.containerMenu.sendAllDataToRemote(); ++ player.containerMenu.forceHeldSlot(context.getHand()); + } + return InteractionResult.PASS; + } 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 37f7b9819f..7e20a847db 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 @@ -101,7 +101,7 @@ + if (placeEvent != null && (placeEvent.isCancelled() || !placeEvent.canBuild())) { + interactionResult = InteractionResult.FAIL; // cancel placement + // PAIL: Remove this when MC-99075 fixed -+ player.containerMenu.sendAllDataToRemote(); ++ player.containerMenu.forceHeldSlot(hand); + serverLevel.capturedTileEntities.clear(); // Paper - Allow chests to be placed with NBT data; clear out block entities as chests and such will pop loot + // revert back all captured blocks + for (org.bukkit.block.BlockState blockstate : blocks) { @@ -192,7 +192,7 @@ return interactionResult; } -@@ -449,31 +_,71 @@ +@@ -449,31 +_,67 @@ return this.isDamageableItem() && this.getDamageValue() >= this.getMaxDamage() - 1; } @@ -212,10 +212,6 @@ + if (i > 0 && player instanceof final ServerPlayer serverPlayer) { // Paper - Add EntityDamageItemEvent - limit to positive damage and run for player + org.bukkit.event.player.PlayerItemDamageEvent event = new org.bukkit.event.player.PlayerItemDamageEvent(serverPlayer.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this), i, originalDamage); // Paper - Add EntityDamageItemEvent + event.getPlayer().getServer().getPluginManager().callEvent(event); -+ -+ if (i != event.getDamage() || event.isCancelled()) { -+ serverPlayer.containerMenu.sendAllDataToRemote(); -+ } + if (event.isCancelled()) { + return; + } diff --git a/paper-server/patches/sources/net/minecraft/world/item/MinecartItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/MinecartItem.java.patch index 1513242757..20602408ae 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/MinecartItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/MinecartItem.java.patch @@ -7,7 +7,7 @@ - serverLevel.addFreshEntity(abstractMinecart); + // CraftBukkit start + if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPlaceEvent(context, abstractMinecart).isCancelled()) { -+ if (context.getPlayer() != null) context.getPlayer().containerMenu.sendAllDataToRemote(); // Paper - Fix inventory desync ++ if (context.getPlayer() != null) context.getPlayer().containerMenu.forceHeldSlot(context.getHand()); // Paper - Fix inventory desync + return InteractionResult.FAIL; + } + // CraftBukkit end diff --git a/paper-server/patches/sources/net/minecraft/world/item/PotionItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/PotionItem.java.patch index f3c68675b2..a48643f35d 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/PotionItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/PotionItem.java.patch @@ -6,7 +6,7 @@ if (context.getClickedFace() != Direction.DOWN && blockState.is(BlockTags.CONVERTABLE_TO_MUD) && potionContents.is(Potions.WATER)) { + // Paper start + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, clickedPos, Blocks.MUD.defaultBlockState())) { -+ player.containerMenu.sendAllDataToRemote(); ++ player.containerMenu.forceHeldSlot(context.getHand()); + return InteractionResult.PASS; + } + // Paper end diff --git a/paper-server/patches/sources/net/minecraft/world/item/ProjectileWeaponItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/ProjectileWeaponItem.java.patch index f0a6f1e7db..fb1e0a6276 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/ProjectileWeaponItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/ProjectileWeaponItem.java.patch @@ -8,7 +8,7 @@ ) { float f = EnchantmentHelper.processProjectileSpread(level, weapon, shooter, 0.0F); float f1 = projectileItems.size() == 1 ? 0.0F : 2.0F * f / (projectileItems.size() - 1); -@@ -62,12 +_,29 @@ +@@ -62,12 +_,26 @@ float f4 = f2 + f3 * ((i + 1) / 2) * f1; f3 = -f3; int i1 = i; @@ -34,9 +34,6 @@ + level, + itemStack + ).isRemoved()) { -+ if (shooter instanceof net.minecraft.server.level.ServerPlayer serverPlayer) { -+ serverPlayer.containerMenu.sendAllDataToRemote(); -+ } + return; + } + } diff --git a/paper-server/patches/sources/net/minecraft/world/item/SnowballItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/SnowballItem.java.patch index ca1757fbfe..5f8381a2e0 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/SnowballItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/SnowballItem.java.patch @@ -25,7 +25,7 @@ + if (event.shouldConsume()) { + itemInHand.consume(1, player); + } else { -+ player.containerMenu.sendAllDataToRemote(); ++ player.containerMenu.forceHeldSlot(hand); + } + // Paper end - PlayerLaunchProjectileEvent + @@ -41,7 +41,7 @@ + ); + // Paper start - PlayerLaunchProjectileEvent - return fail + } else { -+ player.containerMenu.sendAllDataToRemote(); ++ player.containerMenu.forceHeldSlot(hand); + return InteractionResult.FAIL; + } + // Paper end- PlayerLaunchProjectileEvent - return fail diff --git a/paper-server/patches/sources/net/minecraft/world/item/ThrowablePotionItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/ThrowablePotionItem.java.patch index d68ec052a3..998923f073 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/ThrowablePotionItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/ThrowablePotionItem.java.patch @@ -12,10 +12,10 @@ + if (event.shouldConsume()) { + itemInHand.consume(1, player); + } else { -+ player.containerMenu.sendAllDataToRemote(); ++ player.containerMenu.forceHeldSlot(hand); + } + } else { -+ player.containerMenu.sendAllDataToRemote(); ++ player.containerMenu.forceHeldSlot(hand); + return InteractionResult.FAIL; + } + // Paper end - PlayerLaunchProjectileEvent diff --git a/paper-server/patches/sources/net/minecraft/world/item/TridentItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/TridentItem.java.patch index 56b659eb6a..d9a58a9ac4 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/TridentItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/TridentItem.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/item/TridentItem.java +++ b/net/minecraft/world/item/TridentItem.java -@@ -79,18 +_,39 @@ +@@ -79,18 +_,38 @@ .orElse(SoundEvents.TRIDENT_THROW); player.awardStat(Stats.ITEM_USED.get(this)); if (level instanceof ServerLevel serverLevel) { @@ -18,7 +18,6 @@ + if (!event.callEvent() || !tridentDelayed.attemptSpawn()) { + // CraftBukkit start + // Paper end - PlayerLaunchProjectileEvent -+ player.containerMenu.sendAllDataToRemote(); + return false; + } + ThrownTrident thrownTrident = tridentDelayed.projectile(); // Paper - PlayerLaunchProjectileEvent diff --git a/paper-server/patches/sources/net/minecraft/world/item/WindChargeItem.java.patch b/paper-server/patches/sources/net/minecraft/world/item/WindChargeItem.java.patch index 26a26944da..0f46433c55 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/WindChargeItem.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/WindChargeItem.java.patch @@ -16,7 +16,7 @@ + // Paper start - PlayerLaunchProjectileEvent + com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent event = new com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent((org.bukkit.entity.Player) player.getBukkitEntity(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemInHand), (org.bukkit.entity.Projectile) windCharge.projectile().getBukkitEntity()); + if (!event.callEvent() || !windCharge.attemptSpawn()) { -+ player.containerMenu.sendAllDataToRemote(); ++ player.containerMenu.forceHeldSlot(hand); + if (player instanceof net.minecraft.server.level.ServerPlayer serverPlayer) { + serverPlayer.connection.send(new net.minecraft.network.protocol.game.ClientboundCooldownPacket(player.getCooldowns().getCooldownGroup(itemInHand), 0)); // prevent visual desync of cooldown on the slot + } @@ -26,7 +26,7 @@ + player.awardStat(Stats.ITEM_USED.get(this)); + if (event.shouldConsume()) itemInHand.consume(1, player); + else if (!player.hasInfiniteMaterials()) { -+ player.containerMenu.sendAllDataToRemote(); ++ player.containerMenu.forceHeldSlot(hand); + } + // Paper end - PlayerLaunchProjectileEvent } diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/CakeBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/CakeBlock.java.patch index f59bfeee88..cb9c40d7d1 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/CakeBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/CakeBlock.java.patch @@ -6,7 +6,7 @@ if (stack.is(ItemTags.CANDLES) && state.getValue(BITES) == 0 && Block.byItem(item) instanceof CandleBlock candleBlock) { + // Paper start - call change block event + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(player, pos, CandleCakeBlock.byCandle(candleBlock))) { -+ player.containerMenu.sendAllDataToRemote(); // update inv because candle could decrease ++ player.containerMenu.forceHeldSlot(hand); // update inv because candle could decrease + return InteractionResult.TRY_WITH_EMPTY_HAND; + } + // Paper end - call change block event diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/FlowerPotBlock.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/FlowerPotBlock.java.patch index b5078a5bcc..c98f6d64e5 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/FlowerPotBlock.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/FlowerPotBlock.java.patch @@ -11,7 +11,7 @@ + io.papermc.paper.event.player.PlayerFlowerPotManipulateEvent event = new io.papermc.paper.event.player.PlayerFlowerPotManipulateEvent((org.bukkit.entity.Player) player.getBukkitEntity(), block, placedStack, true); + if (!event.callEvent()) { + // Update client -+ player.containerMenu.sendAllDataToRemote(); ++ player.containerMenu.forceHeldSlot(hand); + + return InteractionResult.CONSUME; + } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java index 1d11251009..183d83f023 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java @@ -1489,7 +1489,7 @@ public class CraftEventFactory { BookMeta meta = editBookEvent.getNewBookMeta(); CraftItemStack.setItemMeta(itemInHand, meta); } else { - player.containerMenu.sendAllDataToRemote(); // SPIGOT-7484 + player.containerMenu.forceSlot(player.getInventory(), itemInHandIndex); // SPIGOT-7484 } }