From 310f52293bd1877249bb3b1696cf8dbe1829db95 Mon Sep 17 00:00:00 2001 From: Nassim Jahnke Date: Wed, 12 Mar 2025 12:50:40 +0100 Subject: [PATCH] Add unsupported config option and internal API to simplify remote item matching This is important for 1.21.5 servers/clients and non-Vanilla clients that may not be able to match 1.21.5 data hashes anymore --- .../server/level/ServerPlayer.java.patch | 9 ++++- .../AbstractContainerMenu.java.patch | 35 +++++++++++++++++++ .../ContainerSynchronizer.java.patch | 8 ++++- .../configuration/GlobalConfiguration.java | 2 ++ .../craftbukkit/entity/CraftPlayer.java | 18 ++++++++++ 5 files changed, 70 insertions(+), 2 deletions(-) diff --git a/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch b/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch index ebf5fb8da..a1ac8281e 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/ServerPlayer.java.patch @@ -10,7 +10,7 @@ @Nullable private Vec3 startingToFallPosition; @Nullable -@@ -258,6 +_,13 @@ +@@ -258,6 +_,20 @@ } } @@ -20,6 +20,13 @@ + ServerPlayer.this.connection.send(new ClientboundContainerSetSlotPacket(ServerPlayer.this.inventoryMenu.containerId, ServerPlayer.this.inventoryMenu.incrementStateId(), net.minecraft.world.inventory.InventoryMenu.SHIELD_SLOT, ServerPlayer.this.inventoryMenu.getSlot(net.minecraft.world.inventory.InventoryMenu.SHIELD_SLOT).getItem().copy())); + } + // Paper end - Sync offhand slot in menus ++ ++ // Paper start - add flag to simplify remote matching logic ++ @Override ++ public ServerPlayer player() { ++ return ServerPlayer.this; ++ } ++ // Paper end - add flag to simplify remote matching logic + @Override public void sendSlotChange(AbstractContainerMenu container, int slot, ItemStack itemStack) { 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 6b7e7d70c..e9201236d 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 @@ -72,6 +72,41 @@ } } } +@@ -243,7 +_,7 @@ + private void synchronizeSlotToRemote(int slotIndex, ItemStack stack, Supplier supplier) { + if (!this.suppressRemoteUpdates) { + ItemStack itemStack = this.remoteSlots.get(slotIndex); +- if (!ItemStack.matches(itemStack, stack)) { ++ if (!this.matchesRemote(itemStack, stack)) { // Paper - add flag to simplify remote matching logic + ItemStack itemStack1 = supplier.get(); + this.remoteSlots.set(slotIndex, itemStack1); + if (this.synchronizer != null) { +@@ -267,7 +_,7 @@ + + private void synchronizeCarriedToRemote() { + if (!this.suppressRemoteUpdates) { +- if (!ItemStack.matches(this.getCarried(), this.remoteCarried)) { ++ if (!this.matchesRemote(this.getCarried(), this.remoteCarried)) { // Paper - add flag to simplify remote matching logic + this.remoteCarried = this.getCarried().copy(); + if (this.synchronizer != null) { + this.synchronizer.sendCarriedChange(this, this.remoteCarried); +@@ -276,6 +_,16 @@ + } + } + ++ // Paper start - add flag to simplify remote matching logic ++ private boolean matchesRemote(final ItemStack stack, final ItemStack other) { ++ if (this.synchronizer != null && this.synchronizer.player() != null && this.synchronizer.player().getBukkitEntity().simplifyContainerDesyncCheck()) { ++ // Only check the item type and count ++ return stack == other || (stack.getCount() == other.getCount() && ItemStack.isSameItem(stack, other)); ++ } ++ return ItemStack.matches(stack, other); ++ } ++ // Paper end - add flag to simplify remote matching logic ++ + public void setRemoteSlot(int slot, ItemStack stack) { + this.remoteSlots.set(slot, stack.copy()); + } @@ -343,6 +_,7 @@ this.resetQuickCraft(); } diff --git a/paper-server/patches/sources/net/minecraft/world/inventory/ContainerSynchronizer.java.patch b/paper-server/patches/sources/net/minecraft/world/inventory/ContainerSynchronizer.java.patch index a5f19041a..e5eda2e88 100644 --- a/paper-server/patches/sources/net/minecraft/world/inventory/ContainerSynchronizer.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/inventory/ContainerSynchronizer.java.patch @@ -1,9 +1,15 @@ --- a/net/minecraft/world/inventory/ContainerSynchronizer.java +++ b/net/minecraft/world/inventory/ContainerSynchronizer.java -@@ -11,4 +_,6 @@ +@@ -11,4 +_,12 @@ void sendCarriedChange(AbstractContainerMenu containerMenu, ItemStack stack); void sendDataChange(AbstractContainerMenu container, int id, int value); + + default void sendOffHandSlotChange() {} // Paper - Sync offhand slot in menus ++ ++ // Paper start - add flag to simplify remote matching logic ++ default net.minecraft.server.level.@org.jspecify.annotations.Nullable ServerPlayer player() { ++ return null; ++ } ++ // Paper end - add flag to simplify remote matching logic } diff --git a/paper-server/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java b/paper-server/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java index a35391646..e3a7e6937 100644 --- a/paper-server/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java +++ b/paper-server/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java @@ -186,6 +186,8 @@ public class GlobalConfiguration extends ConfigurationPart { public CompressionFormat compressionFormat = CompressionFormat.ZLIB; @Comment("This setting controls if equipment should be updated when handling certain player actions.") public boolean updateEquipmentOnPlayerActions = true; + @Comment("Only checks an item's amount and type instead of its full data during inventory desync checks.") + public boolean simplifyRemoteItemMatching = false; public enum CompressionFormat { GZIP, diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index 0e0abc233..5050f446e 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -8,6 +8,7 @@ import com.mojang.authlib.GameProfile; import com.mojang.datafixers.util.Pair; import io.netty.buffer.Unpooled; import io.papermc.paper.FeatureHooks; +import io.papermc.paper.configuration.GlobalConfiguration; import io.papermc.paper.entity.LookAnchor; import io.papermc.paper.entity.PaperPlayerGiveResult; import io.papermc.paper.entity.PlayerGiveResult; @@ -225,6 +226,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { private BorderChangeListener clientWorldBorderListener = this.createWorldBorderListener(); public org.bukkit.event.player.PlayerResourcePackStatusEvent.Status resourcePackStatus; // Paper - more resource pack API private static final boolean DISABLE_CHANNEL_LIMIT = System.getProperty("paper.disableChannelLimit") != null; // Paper - add a flag to disable the channel limit + private boolean simplifyContainerDesyncCheck = GlobalConfiguration.get().unsupportedSettings.simplifyRemoteItemMatching; private long lastSaveTime; // Paper - getLastPlayed replacement API public CraftPlayer(CraftServer server, ServerPlayer entity) { @@ -3606,4 +3608,20 @@ public class CraftPlayer extends CraftHumanEntity implements Player { public void setDeathScreenScore(final int score) { getHandle().setScore(score); } + + /** + * Returns whether container desync checks should skip the full item comparison of remote carried and changed slots + * and should instead only check their type and amount. + *

+ * This is useful if the client is not able to produce the same item stack (or as of 1.21.5, its data hashes) as the server. + * + * @return whether to simplify container desync checks + */ + public boolean simplifyContainerDesyncCheck() { + return simplifyContainerDesyncCheck; + } + + public void setSimplifyContainerDesyncCheck(final boolean simplifyContainerDesyncCheck) { + this.simplifyContainerDesyncCheck = simplifyContainerDesyncCheck; + } }