From e371f7951fc3a0f8146423e5bfc864345e96cb61 Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Tue, 15 Jul 2025 14:57:11 +0800 Subject: [PATCH] =?UTF-8?q?fix(bukkit):=20=E4=BF=AE=E5=A4=8D1.21.5+?= =?UTF-8?q?=E7=9A=84=E7=89=A9=E5=93=81=E6=89=8B=E6=84=9F=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/item/BukkitItemManager.java | 12 ++- .../plugin/network/PacketConsumers.java | 76 ++++++++++++++----- .../reflection/minecraft/CoreReflections.java | 23 ++++++ .../reflection/minecraft/MRegistryOps.java | 4 + .../minecraft/NetworkReflections.java | 27 +++++++ gradle.properties | 2 +- 6 files changed, 124 insertions(+), 20 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java index a4c4c12e7..c7708e902 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/BukkitItemManager.java @@ -15,6 +15,7 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistryOps; import net.momirealms.craftengine.bukkit.util.ItemStackUtils; import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.core.entity.player.Player; @@ -39,6 +40,7 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; +import java.util.function.Function; public class BukkitItemManager extends AbstractItemManager { static { @@ -56,8 +58,10 @@ public class BukkitItemManager extends AbstractItemManager { private final Object bedrockItemHolder; private final Item emptyItem; private final UniqueIdItem emptyUniqueItem; + private final Function decoratedHashOpsGenerator; private Set lastRegisteredPatterns = Set.of(); + @SuppressWarnings("unchecked") public BukkitItemManager(BukkitCraftEngine plugin) { super(plugin); instance = this; @@ -68,13 +72,14 @@ public class BukkitItemManager extends AbstractItemManager { this.armorEventListener = new ArmorEventListener(); this.networkItemHandler = VersionHelper.isOrAbove1_20_5() ? new ModernNetworkItemHandler() : new LegacyNetworkItemHandler(); this.registerAllVanillaItems(); - this.bedrockItemHolder = FastNMS.INSTANCE.method$Registry$getHolderByResourceKey(MBuiltInRegistries.ITEM, FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.ITEM, KeyUtils.toResourceLocation(Key.of("minecraft:bedrock")))).get();; + this.bedrockItemHolder = FastNMS.INSTANCE.method$Registry$getHolderByResourceKey(MBuiltInRegistries.ITEM, FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.ITEM, KeyUtils.toResourceLocation(Key.of("minecraft:bedrock")))).get(); this.registerCustomTrimMaterial(); this.loadLastRegisteredPatterns(); ItemStack emptyStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(CoreReflections.instance$ItemStack$EMPTY); this.emptyItem = this.wrap(emptyStack); this.emptyUniqueItem = new UniqueIdItem<>(UniqueKey.AIR, this.emptyItem); + this.decoratedHashOpsGenerator = VersionHelper.isOrAbove1_21_5() ? (Function) FastNMS.INSTANCE.createDecoratedHashOpsGenerator(MRegistryOps.HASHCODE) : null; } @SuppressWarnings("unchecked") @@ -452,4 +457,9 @@ public class BukkitItemManager extends AbstractItemManager { } return this.emptyItem; } + + @Nullable("在 1.21.5+ 才有") + public Function decoratedHashOpsGenerator() { + return decoratedHashOpsGenerator; + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index 109b6bcd3..2bd8a2c27 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -2124,23 +2124,6 @@ public class PacketConsumers { FriendlyByteBuf buf = event.getBuffer(); Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); ItemStack itemStack = FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf); - if (VersionHelper.isOrAbove1_21_5()) { - Item wrapped = BukkitItemManager.instance().wrap(itemStack); - if (!wrapped.isEmpty() && wrapped.isCustomItem()) { - Object containerMenu = FastNMS.INSTANCE.field$Player$containerMenu(serverPlayer.serverPlayer()); - if (containerMenu != null) { - ItemStack carried = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(FastNMS.INSTANCE.method$AbstractContainerMenu$getCarried(containerMenu)); - if (ItemStackUtils.isEmpty(carried)) { - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - Object newFriendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); - FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(newFriendlyBuf, carried); - return; - } - } - } - } BukkitItemManager.instance().s2c(itemStack, serverPlayer).ifPresent((newItemStack) -> { event.setChanged(true); buf.clear(); @@ -2247,7 +2230,64 @@ public class PacketConsumers { public static final BiConsumer CONTAINER_CLICK_1_20 = (user, event) -> { try { - if (VersionHelper.isOrAbove1_21_5()) return; // 1.21.5+需要其他办法解决同步问题 + if (VersionHelper.isOrAbove1_21_5()) { + FriendlyByteBuf buf = event.getBuffer(); + boolean changed = false; + Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); + Object inventory = FastNMS.INSTANCE.method$Player$getInventory(user.serverPlayer()); + int containerId = buf.readContainerId(); + int stateId = buf.readVarInt(); + short slotNum = buf.readShort(); + byte buttonNum = buf.readByte(); + int clickType = buf.readVarInt(); + int i = buf.readVarInt(); + Int2ObjectMap changedSlots = new Int2ObjectOpenHashMap<>(i); + for (int j = 0; j < i; ++j) { + int k = buf.readShort(); + Object hashedStack = FastNMS.INSTANCE.method$StreamDecoder$decode(NetworkReflections.instance$HashedStack$STREAM_CODEC, friendlyBuf); + Object serverSideItemStack = FastNMS.INSTANCE.method$Container$getItem(inventory, k); + Optional optional = BukkitItemManager.instance().s2c(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(serverSideItemStack), ((net.momirealms.craftengine.core.entity.player.Player) user)); + if (optional.isPresent()) { + Object clientSideItemStack = FastNMS.INSTANCE.field$CraftItemStack$handle(optional.get()); + boolean isSync = FastNMS.INSTANCE.method$HashedStack$matches(hashedStack, clientSideItemStack, BukkitItemManager.instance().decoratedHashOpsGenerator()); + if (isSync) { + changed = true; + hashedStack = FastNMS.INSTANCE.method$HashedStack$create(clientSideItemStack, null); + } + } + changedSlots.put(k, hashedStack); + } + Object carriedHashedStack = FastNMS.INSTANCE.method$StreamDecoder$decode(NetworkReflections.instance$HashedStack$STREAM_CODEC, friendlyBuf); + Object containerMenu = FastNMS.INSTANCE.field$Player$containerMenu(user.serverPlayer()); + Object serverSideCarriedItemStack = FastNMS.INSTANCE.method$AbstractContainerMenu$getCarried(containerMenu); + Optional optional = BukkitItemManager.instance().s2c(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(serverSideCarriedItemStack), ((net.momirealms.craftengine.core.entity.player.Player) user)); + if (optional.isPresent()) { + Object clientSideCarriedItemStack = FastNMS.INSTANCE.field$CraftItemStack$handle(optional.get()); + boolean isSync = FastNMS.INSTANCE.method$HashedStack$matches(carriedHashedStack, clientSideCarriedItemStack, BukkitItemManager.instance().decoratedHashOpsGenerator()); + if (isSync) { + changed = true; + carriedHashedStack = FastNMS.INSTANCE.method$HashedStack$create(clientSideCarriedItemStack, BukkitItemManager.instance().decoratedHashOpsGenerator()); + } + } + if (changed) { + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeContainerId(containerId); + buf.writeVarInt(stateId); + buf.writeShort(slotNum); + buf.writeByte(buttonNum); + buf.writeVarInt(clickType); + buf.writeVarInt(changedSlots.size()); + Object newFriendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); + changedSlots.forEach((k, v) -> { + buf.writeShort(k); + FastNMS.INSTANCE.method$StreamEncoder$encode(NetworkReflections.instance$HashedStack$STREAM_CODEC, newFriendlyBuf, v); + }); + FastNMS.INSTANCE.method$StreamEncoder$encode(NetworkReflections.instance$HashedStack$STREAM_CODEC, newFriendlyBuf, carriedHashedStack); + } + return; + } FriendlyByteBuf buf = event.getBuffer(); boolean changed = false; Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java index e99d6d8b6..b8e41241c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/CoreReflections.java @@ -3850,4 +3850,27 @@ public final class CoreReflections { CoreReflections.clazz$BlockHitResult, CoreReflections.clazz$Vec3, CoreReflections.clazz$Direction, CoreReflections.clazz$BlockPos, boolean.class ) ); + + public static final Class clazz$HashOps = MiscUtils.requireNonNullIf( + ReflectionUtils.getClazz(BukkitReflectionUtils.assembleMCClass("util.HashOps")), + VersionHelper.isOrAbove1_21_5() + ); + + public static final Field field$HashOps$CRC32C_INSTANCE = Optional.ofNullable(clazz$HashOps) + .map(it -> ReflectionUtils.getDeclaredField(it, it, 0)) + .orElse(null); + + public static final Object instance$HashOps$CRC32C_INSTANCE; + + static { + try { + if (VersionHelper.isOrAbove1_21_5()) { + instance$HashOps$CRC32C_INSTANCE = field$HashOps$CRC32C_INSTANCE.get(null); + } else { + instance$HashOps$CRC32C_INSTANCE = null; + } + } catch (ReflectiveOperationException e) { + throw new ReflectionInitException("Failed to initialize HashOps", e); + } + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MRegistryOps.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MRegistryOps.java index dd90cb582..f304ef155 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MRegistryOps.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/MRegistryOps.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.bukkit.plugin.reflection.minecraft; +import com.google.common.hash.HashCode; import com.google.gson.JsonElement; import com.mojang.serialization.DynamicOps; import com.mojang.serialization.JsonOps; @@ -12,6 +13,7 @@ import net.momirealms.sparrow.nbt.Tag; import net.momirealms.sparrow.nbt.codec.LegacyJavaOps; import net.momirealms.sparrow.nbt.codec.LegacyNBTOps; import net.momirealms.sparrow.nbt.codec.NBTOps; +import org.jetbrains.annotations.Nullable; import static java.util.Objects.requireNonNull; @@ -21,6 +23,7 @@ public final class MRegistryOps { public static final DynamicOps SPARROW_NBT; public static final DynamicOps JAVA; public static final DynamicOps JSON; + public static final @Nullable("仅在 1.21.5+ 有") DynamicOps HASHCODE; // 1.20.5+ public static final Class clazz$JavaOps = ReflectionUtils.getClazz("com.mojang.serialization.JavaOps"); @@ -47,6 +50,7 @@ public final class MRegistryOps { NBT = (DynamicOps) CoreReflections.method$RegistryOps$create.invoke(null, ReflectionUtils.getDeclaredField(clazz$NbtOps, clazz$NbtOps, 0).get(null), FastNMS.INSTANCE.registryAccess()); JSON = (DynamicOps) CoreReflections.method$RegistryOps$create.invoke(null, JsonOps.INSTANCE, FastNMS.INSTANCE.registryAccess()); SPARROW_NBT = (DynamicOps) CoreReflections.method$RegistryOps$create.invoke(null, VersionHelper.isOrAbove1_20_5() ? NBTOps.INSTANCE : LegacyNBTOps.INSTANCE, FastNMS.INSTANCE.registryAccess()); + HASHCODE = VersionHelper.isOrAbove1_21_5() ? (DynamicOps) CoreReflections.method$RegistryOps$create.invoke(null, CoreReflections.instance$HashOps$CRC32C_INSTANCE, FastNMS.INSTANCE.registryAccess()) : null; } catch (ReflectiveOperationException e) { throw new ReflectionInitException("Failed to init DynamicOps", e); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/NetworkReflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/NetworkReflections.java index 9b5377f07..6c1d427a9 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/NetworkReflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/reflection/minecraft/NetworkReflections.java @@ -1594,4 +1594,31 @@ public final class NetworkReflections { List.of("network.protocol.common.ClientboundUpdateTagsPacket", "network.protocol.game.ClientboundUpdateTagsPacket") ) ); + + // 1.21.5+ + public static final Class clazz$HashedStack = MiscUtils.requireNonNullIf( + ReflectionUtils.getClazz( + BukkitReflectionUtils.assembleMCClass("network.HashedStack") + ), + VersionHelper.isOrAbove1_21_5() + ); + + // 1.21.5+ + public static final Field field$HashedStack$STREAM_CODEC = Optional.ofNullable(clazz$HashedStack) + .map(it -> ReflectionUtils.getDeclaredField(it, clazz$StreamCodec, 0)) + .orElse(null); + + public static final Object instance$HashedStack$STREAM_CODEC; + + static { + try { + if (VersionHelper.isOrAbove1_21_5()) { + instance$HashedStack$STREAM_CODEC = field$HashedStack$STREAM_CODEC.get(null); + } else { + instance$HashedStack$STREAM_CODEC = null; + } + } catch (ReflectiveOperationException e) { + throw new ReflectionInitException("Failed to initialize HashedStack$STREAM_CODEC", e); + } + } } diff --git a/gradle.properties b/gradle.properties index 6c5b9b256..af7ef18be 100644 --- a/gradle.properties +++ b/gradle.properties @@ -50,7 +50,7 @@ byte_buddy_version=1.17.5 ahocorasick_version=0.6.3 snake_yaml_version=2.4 anti_grief_version=0.18 -nms_helper_version=1.0.34 +nms_helper_version=1.0.35 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.31.23