From 2b8f103d4b5031e5e4afe1bf8ea355354edb026c Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Wed, 28 May 2025 02:47:55 +0800 Subject: [PATCH 1/7] =?UTF-8?q?feat(network):=20=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF=E4=BE=A7=E7=89=A9=E5=93=81=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/network/BukkitNetworkManager.java | 25 +++++++++++++------ .../plugin/network/PacketConsumers.java | 5 ++-- .../plugin/network/id/PacketIdFinder.java | 20 +++++++++++++++ .../craftengine/bukkit/util/Reflections.java | 2 +- 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index 1bb9a7944..b874b9d27 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -9,6 +9,7 @@ import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; +import net.momirealms.craftengine.bukkit.plugin.network.id.PacketIdFinder; import net.momirealms.craftengine.bukkit.plugin.network.id.PacketIds1_20; import net.momirealms.craftengine.bukkit.plugin.network.id.PacketIds1_20_5; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; @@ -40,9 +41,8 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes private static BukkitNetworkManager instance; private static final Map, TriConsumer> NMS_PACKET_HANDLERS = new HashMap<>(); // only for game stage for the moment - // todo 优化成 数组 - private static final Map> S2C_BYTE_BUFFER_PACKET_HANDLERS = new HashMap<>(); - private static final Map> C2S_BYTE_BUFFER_PACKET_HANDLERS = new HashMap<>(); + private static BiConsumer[] S2C_BYTE_BUFFER_PACKET_HANDLERS; + private static BiConsumer[] C2S_BYTE_BUFFER_PACKET_HANDLERS; private static void registerNMSPacketConsumer(final TriConsumer function, @Nullable Class packet) { if (packet == null) return; @@ -51,12 +51,18 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes private static void registerS2CByteBufPacketConsumer(final BiConsumer function, int id) { if (id == -1) return; - S2C_BYTE_BUFFER_PACKET_HANDLERS.put(id, function); + if (id < 0 || id >= S2C_BYTE_BUFFER_PACKET_HANDLERS.length) { + throw new IllegalArgumentException("Invalid packet id: " + id); + } + S2C_BYTE_BUFFER_PACKET_HANDLERS[id] = function; } private static void registerC2SByteBufPacketConsumer(final BiConsumer function, int id) { if (id == -1) return; - C2S_BYTE_BUFFER_PACKET_HANDLERS.put(id, function); + if (id < 0 || id >= C2S_BYTE_BUFFER_PACKET_HANDLERS.length) { + throw new IllegalArgumentException("Invalid packet id: " + id); + } + C2S_BYTE_BUFFER_PACKET_HANDLERS[id] = function; } private final BiConsumer> packetsConsumer; @@ -81,8 +87,13 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes private static boolean hasModelEngine; private static boolean hasViaVersion; + @SuppressWarnings("unchecked") public BukkitNetworkManager(BukkitCraftEngine plugin) { instance = this; + S2C_BYTE_BUFFER_PACKET_HANDLERS = new BiConsumer[PacketIdFinder.maxS2CPacketId() + 1]; + C2S_BYTE_BUFFER_PACKET_HANDLERS = new BiConsumer[PacketIdFinder.maxC2SPacketId() + 1]; + Arrays.fill(S2C_BYTE_BUFFER_PACKET_HANDLERS, (BiConsumer) (user, event) -> {}); + Arrays.fill(C2S_BYTE_BUFFER_PACKET_HANDLERS, (BiConsumer) (user, event) -> {}); hasModelEngine = Bukkit.getPluginManager().getPlugin("ModelEngine") != null; hasViaVersion = Bukkit.getPluginManager().getPlugin("ViaVersion") != null; this.plugin = plugin; @@ -647,13 +658,13 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes protected void handleS2CByteBufPacket(NetWorkUser user, ByteBufPacketEvent event) { int packetID = event.packetID(); - Optional.ofNullable(S2C_BYTE_BUFFER_PACKET_HANDLERS.get(packetID)) + Optional.ofNullable(S2C_BYTE_BUFFER_PACKET_HANDLERS[packetID]) .ifPresent(function -> function.accept(user, event)); } protected void handleC2SByteBufPacket(NetWorkUser user, ByteBufPacketEvent event) { int packetID = event.packetID(); - Optional.ofNullable(C2S_BYTE_BUFFER_PACKET_HANDLERS.get(packetID)) + Optional.ofNullable(C2S_BYTE_BUFFER_PACKET_HANDLERS[packetID]) .ifPresent(function -> function.accept(user, event)); } 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 7171b67f2..1f13e2383 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 @@ -2140,11 +2140,12 @@ public class PacketConsumers { 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$FriendlyByteBuf$writeItem(buf, v); + FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(newFriendlyBuf, v); }); - FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(buf, carriedItem); + FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(newFriendlyBuf, carriedItem); } } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ServerboundContainerClickPacket", e); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIdFinder.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIdFinder.java index cff1f93b8..c399ea948 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIdFinder.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/id/PacketIdFinder.java @@ -6,12 +6,15 @@ import net.momirealms.craftengine.bukkit.util.Reflections; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.VersionHelper; +import java.util.Collections; import java.util.HashMap; import java.util.Map; public class PacketIdFinder { private static final Map> gamePacketIdsByName = new HashMap<>(); private static final Map, Integer>> gamePacketIdsByClazz = new HashMap<>(); + private static final int maxC2SPacketId; + private static final int maxS2CPacketId; static { try { @@ -34,6 +37,23 @@ public class PacketIdFinder { } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to get packets", e); } + maxS2CPacketId = calculateMaxId("clientbound"); + maxC2SPacketId = calculateMaxId("serverbound"); + } + private static int calculateMaxId(String direction) { + if (VersionHelper.isOrAbove1_20_5()) { + return gamePacketIdsByName.getOrDefault(direction, Collections.emptyMap()).size(); + } else { + return gamePacketIdsByClazz.getOrDefault(direction, Collections.emptyMap()).size(); + } + } + + public static int maxC2SPacketId() { + return maxC2SPacketId; + } + + public static int maxS2CPacketId() { + return maxS2CPacketId; } public static int clientboundByName(String packetName) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java index fba7a0f92..d07de7afe 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java @@ -41,7 +41,7 @@ import java.util.function.Consumer; import static java.util.Objects.requireNonNull; -@SuppressWarnings("unused") +@SuppressWarnings({"unused", "unchecked"}) public class Reflections { public static void init() { From dd03f5f922a9b5f5e278d0d729d9d1cbb5f98800 Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Wed, 28 May 2025 04:45:38 +0800 Subject: [PATCH 2/7] =?UTF-8?q?feat(network):=20=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF=E4=BE=A7=E7=89=A9=E5=93=81=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/network/BukkitNetworkManager.java | 14 +++- .../plugin/network/PacketConsumers.java | 33 ++++++---- .../handler/CommonItemPacketHandler.java | 53 +++++++++++++++ .../handler/ItemDisplayPacketHandler.java | 53 +++++++++++++++ .../bukkit/util/EntityDataUtils.java | 2 + .../craftengine/bukkit/util/Reflections.java | 65 +++++++++++++++++++ 6 files changed, 204 insertions(+), 16 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/CommonItemPacketHandler.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemDisplayPacketHandler.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index b874b9d27..232ac9476 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -92,8 +92,8 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes instance = this; S2C_BYTE_BUFFER_PACKET_HANDLERS = new BiConsumer[PacketIdFinder.maxS2CPacketId() + 1]; C2S_BYTE_BUFFER_PACKET_HANDLERS = new BiConsumer[PacketIdFinder.maxC2SPacketId() + 1]; - Arrays.fill(S2C_BYTE_BUFFER_PACKET_HANDLERS, (BiConsumer) (user, event) -> {}); - Arrays.fill(C2S_BYTE_BUFFER_PACKET_HANDLERS, (BiConsumer) (user, event) -> {}); + Arrays.fill(S2C_BYTE_BUFFER_PACKET_HANDLERS, Handlers.DO_NOTHING); + Arrays.fill(C2S_BYTE_BUFFER_PACKET_HANDLERS, Handlers.DO_NOTHING); hasModelEngine = Bukkit.getPluginManager().getPlugin("ModelEngine") != null; hasViaVersion = Bukkit.getPluginManager().getPlugin("ViaVersion") != null; this.plugin = plugin; @@ -710,4 +710,14 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes } return output; } + + @FunctionalInterface + public interface Handlers extends BiConsumer { + Handlers DO_NOTHING = doNothing(); + + static Handlers doNothing() { + return (user, byteBufPacketEvent) -> { + }; + } + } } 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 399ad9d47..d3d473421 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 @@ -73,15 +73,15 @@ import java.util.*; import java.util.function.BiConsumer; public class PacketConsumers { - private static AddEntityHandler[] ADD_ENTITY_HANDLERS; + private static BukkitNetworkManager.Handlers[] ADD_ENTITY_HANDLERS; private static int[] mappings; private static int[] mappingsMOD; private static IntIdentityList BLOCK_LIST; private static IntIdentityList BIOME_LIST; public static void initEntities(int registrySize) { - ADD_ENTITY_HANDLERS = new AddEntityHandler[registrySize]; - Arrays.fill(ADD_ENTITY_HANDLERS, AddEntityHandler.DO_NOTHING); + ADD_ENTITY_HANDLERS = new BukkitNetworkManager.Handlers[registrySize]; + Arrays.fill(ADD_ENTITY_HANDLERS, BukkitNetworkManager.Handlers.DO_NOTHING); ADD_ENTITY_HANDLERS[Reflections.instance$EntityType$FALLING_BLOCK$registryId] = (user, event) -> { FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); @@ -121,6 +121,21 @@ public class PacketConsumers { ADD_ENTITY_HANDLERS[Reflections.instance$EntityType$BLOCK_DISPLAY$registryId] = simpleAddEntityHandler(BlockDisplayPacketHandler.INSTANCE); ADD_ENTITY_HANDLERS[Reflections.instance$EntityType$TEXT_DISPLAY$registryId] = simpleAddEntityHandler(TextDisplayPacketHandler.INSTANCE); ADD_ENTITY_HANDLERS[Reflections.instance$EntityType$ARMOR_STAND$registryId] = simpleAddEntityHandler(ArmorStandPacketHandler.INSTANCE); + ADD_ENTITY_HANDLERS[Reflections.instance$EntityType$ITEM_DISPLAY$registryId] = simpleAddEntityHandler(ItemDisplayPacketHandler.INSTANCE); + ADD_ENTITY_HANDLERS[Reflections.instance$EntityType$FIREBALL$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE); + ADD_ENTITY_HANDLERS[Reflections.instance$EntityType$EYE_OF_ENDER$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE); + ADD_ENTITY_HANDLERS[Reflections.instance$EntityType$FIREWORK_ROCKET$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE); + ADD_ENTITY_HANDLERS[Reflections.instance$EntityType$ITEM$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE); + ADD_ENTITY_HANDLERS[Reflections.instance$EntityType$ITEM_FRAME$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE); + ADD_ENTITY_HANDLERS[Reflections.instance$EntityType$SMALL_FIREBALL$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE); + ADD_ENTITY_HANDLERS[Reflections.instance$EntityType$EGG$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE); + ADD_ENTITY_HANDLERS[Reflections.instance$EntityType$ENDER_PEARL$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE); + ADD_ENTITY_HANDLERS[Reflections.instance$EntityType$EXPERIENCE_BOTTLE$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE); + ADD_ENTITY_HANDLERS[Reflections.instance$EntityType$SNOWBALL$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE); + ADD_ENTITY_HANDLERS[Reflections.instance$EntityType$POTION$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE); + if (VersionHelper.isOrAbove1_20_5()) { + ADD_ENTITY_HANDLERS[Reflections.instance$EntityType$OMINOUS_ITEM_SPAWNER$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE); + } } public static void initBlocks(Map map, int registrySize) { @@ -2271,17 +2286,7 @@ public class PacketConsumers { } }; - @FunctionalInterface - public interface AddEntityHandler extends BiConsumer { - AddEntityHandler DO_NOTHING = doNothing(); - - static AddEntityHandler doNothing() { - return (user, byteBufPacketEvent) -> { - }; - } - } - - private static AddEntityHandler simpleAddEntityHandler(EntityPacketHandler handler) { + private static BukkitNetworkManager.Handlers simpleAddEntityHandler(EntityPacketHandler handler) { return (user, event) -> { FriendlyByteBuf buf = event.getBuffer(); user.entityPacketHandlers().put(buf.readVarInt(), handler); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/CommonItemPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/CommonItemPacketHandler.java new file mode 100644 index 000000000..a17a53691 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/CommonItemPacketHandler.java @@ -0,0 +1,53 @@ +package net.momirealms.craftengine.bukkit.plugin.network.handler; + +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; +import net.momirealms.craftengine.bukkit.util.EntityDataUtils; +import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; +import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; +import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.util.FriendlyByteBuf; +import org.bukkit.inventory.ItemStack; + +import java.util.List; +import java.util.Optional; + +public class CommonItemPacketHandler implements EntityPacketHandler { + public static final CommonItemPacketHandler INSTANCE = new CommonItemPacketHandler(); + + @Override + public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + FriendlyByteBuf buf = event.getBuffer(); + ItemBuildContext context = ItemBuildContext.of((BukkitServerPlayer) user); + int id = buf.readVarInt(); + boolean isChanged = false; + List packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf); + for (int i = 0; i < packedItems.size(); i++) { + Object packedItem = packedItems.get(i); + int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem); + if (entityDataId == EntityDataUtils.ITEM_DATA_ID) { + Object nmsItemStack = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); + ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsItemStack); + Optional optional = BukkitItemManager.instance().s2c(itemStack, context); + if (optional.isPresent()) { + isChanged = true; + itemStack = optional.get(); + Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); + packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue( + entityDataId, serializer, FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack) + )); + break; + } + } + } + if (isChanged) { + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeVarInt(id); + FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(packedItems, buf); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemDisplayPacketHandler.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemDisplayPacketHandler.java new file mode 100644 index 000000000..3ffba123c --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/handler/ItemDisplayPacketHandler.java @@ -0,0 +1,53 @@ +package net.momirealms.craftengine.bukkit.plugin.network.handler; + +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; +import net.momirealms.craftengine.bukkit.util.EntityDataUtils; +import net.momirealms.craftengine.core.item.ItemBuildContext; +import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent; +import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler; +import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.util.FriendlyByteBuf; +import org.bukkit.inventory.ItemStack; + +import java.util.List; +import java.util.Optional; + +public class ItemDisplayPacketHandler implements EntityPacketHandler { + public static final ItemDisplayPacketHandler INSTANCE = new ItemDisplayPacketHandler(); + + @Override + public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) { + FriendlyByteBuf buf = event.getBuffer(); + ItemBuildContext context = ItemBuildContext.of((BukkitServerPlayer) user); + int id = buf.readVarInt(); + boolean isChanged = false; + List packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf); + for (int i = 0; i < packedItems.size(); i++) { + Object packedItem = packedItems.get(i); + int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem); + if (entityDataId == EntityDataUtils.DISPLAYED_ITEM_DATA_ID) { + Object nmsItemStack = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem); + ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsItemStack); + Optional optional = BukkitItemManager.instance().s2c(itemStack, context); + if (optional.isPresent()) { + isChanged = true; + itemStack = optional.get(); + Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem); + packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue( + entityDataId, serializer, FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(itemStack) + )); + break; + } + } + } + if (isChanged) { + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeVarInt(id); + FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(packedItems, buf); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityDataUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityDataUtils.java index 8c70d577f..715c28741 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityDataUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/EntityDataUtils.java @@ -13,7 +13,9 @@ public class EntityDataUtils { private static final int RIGHT_ALIGNMENT = 0x10; // 16 public static final int BLOCK_STATE_DATA_ID = VersionHelper.isOrAbove1_20_2() ? 23 : 22; public static final int TEXT_DATA_ID = VersionHelper.isOrAbove1_20_2() ? 23 : 22; + public static final int DISPLAYED_ITEM_DATA_ID = VersionHelper.isOrAbove1_20_2() ? 23 : 22; public static final int CUSTOM_NAME_DATA_ID = 2; + public static final int ITEM_DATA_ID = 8; public static byte encodeTextDisplayMask(boolean hasShadow, boolean isSeeThrough, boolean useDefaultBackground, int alignment) { int bitMask = 0; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java index d07de7afe..582eeda4c 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/Reflections.java @@ -3846,6 +3846,17 @@ public class Reflections { public static final Object instance$EntityType$OAK_BOAT; public static final Object instance$EntityType$TRIDENT; public static final Object instance$EntityType$SNOWBALL; + public static final Object instance$EntityType$FIREBALL; + public static final Object instance$EntityType$EYE_OF_ENDER; + public static final Object instance$EntityType$FIREWORK_ROCKET; + public static final Object instance$EntityType$ITEM; + public static final Object instance$EntityType$ITEM_FRAME; + public static final Object instance$EntityType$OMINOUS_ITEM_SPAWNER; + public static final Object instance$EntityType$SMALL_FIREBALL; + public static final Object instance$EntityType$EGG; + public static final Object instance$EntityType$ENDER_PEARL; + public static final Object instance$EntityType$EXPERIENCE_BOTTLE; + public static final Object instance$EntityType$POTION; static { try { @@ -3869,6 +3880,32 @@ public class Reflections { instance$EntityType$TRIDENT = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, trident); Object snowball = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "snowball"); instance$EntityType$SNOWBALL = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, snowball); + Object fireball = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "fireball"); + instance$EntityType$FIREBALL = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, fireball); + Object eyeOfEnder = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "eye_of_ender"); + instance$EntityType$EYE_OF_ENDER = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, eyeOfEnder); + Object fireworkRocket = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "firework_rocket"); + instance$EntityType$FIREWORK_ROCKET = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, fireworkRocket); + Object item = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "item"); + instance$EntityType$ITEM = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, item); + Object itemFrame = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "item_frame"); + instance$EntityType$ITEM_FRAME = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, itemFrame); + Object smallFireball = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "small_fireball"); + instance$EntityType$SMALL_FIREBALL = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, smallFireball); + Object egg = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "egg"); + instance$EntityType$EGG = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, egg); + Object enderPearl = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "ender_pearl"); + instance$EntityType$ENDER_PEARL = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, enderPearl); + Object experienceBottle = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "experience_bottle"); + instance$EntityType$EXPERIENCE_BOTTLE = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, experienceBottle); + Object potion = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "potion"); + instance$EntityType$POTION = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, potion); + if (VersionHelper.isOrAbove1_20_5()) { + Object ominousItemSpawner = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "ominous_item_spawner"); + instance$EntityType$OMINOUS_ITEM_SPAWNER = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, ominousItemSpawner); + } else { + instance$EntityType$OMINOUS_ITEM_SPAWNER = null; + } } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } @@ -6451,6 +6488,18 @@ public class Reflections { public static final int instance$EntityType$FALLING_BLOCK$registryId; public static final int instance$EntityType$TRIDENT$registryId; public static final int instance$EntityType$ARMOR_STAND$registryId; + public static final int instance$EntityType$FIREBALL$registryId; + public static final int instance$EntityType$EYE_OF_ENDER$registryId; + public static final int instance$EntityType$FIREWORK_ROCKET$registryId; + public static final int instance$EntityType$ITEM$registryId; + public static final int instance$EntityType$ITEM_FRAME$registryId; + public static final int instance$EntityType$OMINOUS_ITEM_SPAWNER$registryId; + public static final int instance$EntityType$SMALL_FIREBALL$registryId; + public static final int instance$EntityType$EGG$registryId; + public static final int instance$EntityType$ENDER_PEARL$registryId; + public static final int instance$EntityType$EXPERIENCE_BOTTLE$registryId; + public static final int instance$EntityType$SNOWBALL$registryId; + public static final int instance$EntityType$POTION$registryId; static { try { @@ -6460,6 +6509,22 @@ public class Reflections { instance$EntityType$FALLING_BLOCK$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$FALLING_BLOCK); instance$EntityType$TRIDENT$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$TRIDENT); instance$EntityType$ARMOR_STAND$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$ARMOR_STAND); + instance$EntityType$FIREBALL$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$FIREBALL); + instance$EntityType$EYE_OF_ENDER$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$EYE_OF_ENDER); + instance$EntityType$FIREWORK_ROCKET$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$FIREWORK_ROCKET); + instance$EntityType$ITEM$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$ITEM); + instance$EntityType$ITEM_FRAME$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$ITEM_FRAME); + instance$EntityType$SMALL_FIREBALL$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$SMALL_FIREBALL); + instance$EntityType$EGG$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$EGG); + instance$EntityType$ENDER_PEARL$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$ENDER_PEARL); + instance$EntityType$EXPERIENCE_BOTTLE$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$EXPERIENCE_BOTTLE); + instance$EntityType$SNOWBALL$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$SNOWBALL); + instance$EntityType$POTION$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$POTION); + if (VersionHelper.isOrAbove1_20_5()) { + instance$EntityType$OMINOUS_ITEM_SPAWNER$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$OMINOUS_ITEM_SPAWNER); + } else { + instance$EntityType$OMINOUS_ITEM_SPAWNER$registryId = -1; + } } catch (Exception e) { throw new RuntimeException(e); } From 3046ba83f2aee1c8d299670b412ed3389d7896e9 Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Wed, 28 May 2025 05:25:01 +0800 Subject: [PATCH 3/7] =?UTF-8?q?feat(network):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/default/configuration/items.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/bukkit/loader/src/main/resources/resources/default/configuration/items.yml b/bukkit/loader/src/main/resources/resources/default/configuration/items.yml index 923daec69..7337065b6 100644 --- a/bukkit/loader/src/main/resources/resources/default/configuration/items.yml +++ b/bukkit/loader/src/main/resources/resources/default/configuration/items.yml @@ -101,11 +101,26 @@ items#topaz_gears: settings: tags: - "default:topaz_tools" + client-bound-data: + components: + can_break: + blocks: minecraft:note_block + state: + "instrument": "hat" + "note": + "min": "0" + "max": "2" + "powered": "false" data: item-name: "<#FF8C00>" tooltip-style: minecraft:topaz components: minecraft:max_damage: 64 + can_break: + blocks: + - "craftengine:note_block_0" + - "craftengine:note_block_1" + - "craftengine:note_block_2" model: template: default:model/simplified_handheld arguments: From 745dbf57f8f531a91b3cac88052cf4d4e568b3d5 Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Wed, 28 May 2025 05:58:28 +0800 Subject: [PATCH 4/7] =?UTF-8?q?refactor(bukkit/client-mod):=E4=BC=98?= =?UTF-8?q?=E5=8C=96=20NetWorkDataTypes=20=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bukkit/plugin/network/payload/NetWorkDataTypes.java | 2 +- .../craftengine/fabric/client/util/NetWorkDataTypes.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkDataTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkDataTypes.java index 281d0db70..5655e1028 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkDataTypes.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkDataTypes.java @@ -57,7 +57,7 @@ public class NetWorkDataTypes { return id2NetWorkDataTypes.get(id); } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "unused"}) public NetWorkDataTypes as(Class clazz) { return (NetWorkDataTypes) this; } diff --git a/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/util/NetWorkDataTypes.java b/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/util/NetWorkDataTypes.java index 63c8bf8be..e1276a008 100644 --- a/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/util/NetWorkDataTypes.java +++ b/client-mod/src/client/java/net/momirealms/craftengine/fabric/client/util/NetWorkDataTypes.java @@ -56,7 +56,7 @@ public class NetWorkDataTypes { return id2NetWorkDataTypes.get(id); } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "unused"}) public NetWorkDataTypes as(Class clazz) { return (NetWorkDataTypes) this; } From 4da091e0e2ffa272b86c235c15769c4ce7d1915a Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Wed, 28 May 2025 07:48:43 +0800 Subject: [PATCH 5/7] =?UTF-8?q?feat(network):=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E7=BD=91=E7=BB=9C=E7=BC=96=E8=A7=A3=E7=A0=81=E5=99=A8=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E5=92=8C=E9=80=9A=E7=94=A8=E7=BC=96=E8=A7=A3=E7=A0=81?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/network/payload/NetWorkCodec.java | 22 + .../plugin/network/payload/NetWorkCodecs.java | 394 ++++++++++++++++++ .../network/payload/NetWorkDecoder.java | 7 + .../network/payload/NetWorkEncoder.java | 7 + 4 files changed, 430 insertions(+) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkCodec.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkCodecs.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkDecoder.java create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkEncoder.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkCodec.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkCodec.java new file mode 100644 index 000000000..7fa1d4131 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkCodec.java @@ -0,0 +1,22 @@ +package net.momirealms.craftengine.bukkit.plugin.network.payload; + +import io.netty.buffer.ByteBuf; + +import java.util.function.Function; + +public interface NetWorkCodec extends NetWorkEncoder, NetWorkDecoder { + + default NetWorkCodec map(Function factory, Function getter) { + return new NetWorkCodec<>() { + @Override + public O decode(ByteBuf in) { + return factory.apply(NetWorkCodec.this.decode(in)); + } + + @Override + public void encode(ByteBuf out, O value) { + NetWorkCodec.this.encode(out, getter.apply(value)); + } + }; + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkCodecs.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkCodecs.java new file mode 100644 index 000000000..4642578bc --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkCodecs.java @@ -0,0 +1,394 @@ +package net.momirealms.craftengine.bukkit.plugin.network.payload; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufInputStream; +import io.netty.buffer.ByteBufOutputStream; +import io.netty.buffer.ByteBufUtil; +import io.netty.handler.codec.DecoderException; +import io.netty.handler.codec.EncoderException; +import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.sparrow.nbt.CompoundTag; +import net.momirealms.sparrow.nbt.NBT; +import net.momirealms.sparrow.nbt.Tag; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Optional; +import java.util.OptionalInt; + +/** + * 随便写了点方便后面重构和客户端通讯 + */ +public interface NetWorkCodecs { + + NetWorkCodec BOOLEAN = new NetWorkCodec<>() { + @Override + public Boolean decode(ByteBuf in) { + return in.readBoolean(); + } + + @Override + public void encode(ByteBuf out, Boolean value) { + out.writeBoolean(value); + } + }; + + NetWorkCodec BYTE = new NetWorkCodec<>() { + @Override + public Byte decode(ByteBuf in) { + return in.readByte(); + } + + @Override + public void encode(ByteBuf out, Byte value) { + out.writeByte(value); + } + }; + + NetWorkCodec ROTATION_BYTE = BYTE.map(MCUtils::unpackDegrees, MCUtils::packDegrees); + + NetWorkCodec SHORT = new NetWorkCodec<>() { + @Override + public Short decode(ByteBuf in) { + return in.readShort(); + } + + @Override + public void encode(ByteBuf out, Short value) { + out.writeShort(value); + } + }; + + NetWorkCodec UNSIGNED_SHORT = new NetWorkCodec<>() { + @Override + public Integer decode(ByteBuf in) { + return in.readUnsignedShort(); + } + + @Override + public void encode(ByteBuf out, Integer value) { + out.writeShort(value); + } + }; + + NetWorkCodec INTEGER = new NetWorkCodec<>() { + @Override + public Integer decode(ByteBuf in) { + return in.readInt(); + } + + @Override + public void encode(ByteBuf out, Integer value) { + out.writeInt(value); + } + }; + + NetWorkCodec VAR_INTEGER = new NetWorkCodec<>() { + @Override + public Integer decode(ByteBuf in) { + int result = 0; + int bytesRead = 0; + byte currentByte; + do { + currentByte = in.readByte(); + result |= (currentByte & 127) << bytesRead++ * 7; + if (bytesRead > 5) { + throw new RuntimeException("VarInt too big"); + } + } while ((currentByte & 128) == 128); + return result; + } + + @Override + public void encode(ByteBuf out, Integer value) { + while ((value & -128) != 0) { + out.writeByte(value & 127 | 128); + value >>>= 7; + } + out.writeByte(value); + } + }; + + NetWorkCodec OPTIONAL_VAR_INTEGER = VAR_INTEGER.map( + integer -> integer == 0 ? OptionalInt.empty() : OptionalInt.of(integer - 1), + optionalInt -> optionalInt.isPresent() ? optionalInt.getAsInt() + 1 : 0 + ); + + NetWorkCodec LONG = new NetWorkCodec<>() { + @Override + public Long decode(ByteBuf in) { + return in.readLong(); + } + + @Override + public void encode(ByteBuf out, Long value) { + out.writeLong(value); + } + }; + + NetWorkCodec VAR_LONG = new NetWorkCodec<>() { + @Override + public Long decode(ByteBuf in) { + long result = 0L; + int bytesRead = 0; + byte currentByte; + do { + currentByte = in.readByte(); + result |= (long)(currentByte & 127) << bytesRead++ * 7; + if (bytesRead > 10) { + throw new RuntimeException("VarLong too big"); + } + } while ((currentByte & 128) == 128); + return result; + } + + @Override + public void encode(ByteBuf out, Long value) { + while ((value & -128L) != 0L) { + out.writeByte((int)(value & 127L) | 128); + value >>>= 7; + } + out.writeByte(value.intValue()); + } + }; + + NetWorkCodec FLOAT = new NetWorkCodec<>() { + @Override + public Float decode(ByteBuf in) { + return in.readFloat(); + } + + @Override + public void encode(ByteBuf out, Float value) { + out.writeFloat(value); + } + }; + + NetWorkCodec DOUBLE = new NetWorkCodec<>() { + @Override + public Double decode(ByteBuf in) { + return in.readDouble(); + } + + @Override + public void encode(ByteBuf out, Double value) { + out.writeDouble(value); + } + }; + + NetWorkCodec BYTE_ARRAY = new NetWorkCodec<>() { + @Override + public byte[] decode(ByteBuf in) { + int maxSize = in.readableBytes(); + int size = VAR_INTEGER.decode(in); + if (size > maxSize) { + throw new DecoderException("ByteArray with size " + size + " is bigger than allowed " + maxSize); + } else { + byte[] bytes = new byte[size]; + in.readBytes(bytes); + return bytes; + } + } + + @Override + public void encode(ByteBuf out, byte[] value) { + VAR_INTEGER.encode(out, value.length); + out.writeBytes(value); + } + }; + + NetWorkCodec LONG_ARRAY = new NetWorkCodec<>() { + @Override + public long[] decode(ByteBuf in) { + int arrayLength = VAR_INTEGER.decode(in); + int maxPossibleElements = in.readableBytes() / 8; + if (arrayLength > maxPossibleElements) { + throw new DecoderException("LongArray with size " + arrayLength + " is bigger than allowed " + maxPossibleElements); + } else { + long[] longArray = new long[arrayLength]; + for (int i = 0; i < longArray.length; i++) { + longArray[i] = in.readLong(); + } + return longArray; + } + } + + @Override + public void encode(ByteBuf out, long[] value) { + VAR_INTEGER.encode(out, value.length); + for (long element : value) { + out.writeLong(element); + } + } + }; + + NetWorkCodec STRING_UTF8 = new NetWorkCodec<>() { + private static final int MAX_STRING_LENGTH = 32767; + + @Override + public String decode(ByteBuf in) { + int maxEncodedBytes = ByteBufUtil.utf8MaxBytes(MAX_STRING_LENGTH); + int encodedLength = VAR_INTEGER.decode(in); + if (encodedLength > maxEncodedBytes) { + throw new DecoderException("The received encoded string buffer length is longer than maximum allowed (" + encodedLength + " > " + maxEncodedBytes + ")"); + } else if (encodedLength < 0) { + throw new DecoderException("The received encoded string buffer length is less than zero! Weird string!"); + } else { + int availableBytes = in.readableBytes(); + if (encodedLength > availableBytes) { + throw new DecoderException("Not enough bytes in buffer, expected " + encodedLength + ", but got " + availableBytes); + } else { + String decodedString = in.toString(in.readerIndex(), encodedLength, StandardCharsets.UTF_8); + in.readerIndex(in.readerIndex() + encodedLength); + if (decodedString.length() > MAX_STRING_LENGTH) { + throw new DecoderException("The received string length is longer than maximum allowed (" + decodedString.length() + " > " + MAX_STRING_LENGTH + ")"); + } else { + return decodedString; + } + } + } + } + + @Override + public void encode(ByteBuf out, String value) { + if (value.length() > MAX_STRING_LENGTH) { + throw new EncoderException("String too big (was " + value.length() + " characters, max " + MAX_STRING_LENGTH + ")"); + } else { + int maxPossibleBytes = ByteBufUtil.utf8MaxBytes(value); + ByteBuf tempBuffer = out.alloc().buffer(maxPossibleBytes); + try { + int actualEncodedBytes = ByteBufUtil.writeUtf8(tempBuffer, value); + int maxAllowedBytes = ByteBufUtil.utf8MaxBytes(MAX_STRING_LENGTH); + if (actualEncodedBytes > maxAllowedBytes) { + throw new EncoderException("String too big (was " + actualEncodedBytes + " bytes encoded, max " + maxAllowedBytes + ")"); + } + VAR_INTEGER.encode(out, actualEncodedBytes); + out.writeBytes(tempBuffer); + } finally { + tempBuffer.release(); + } + } + } + }; + + NetWorkCodec TAG = new NetWorkCodec<>() { + @Override + public Tag decode(ByteBuf in) { + int initialIndex = in.readerIndex(); + byte marker = in.readByte(); + if (marker == 0) { + return null; + } else { + in.readerIndex(initialIndex); + try { + return NBT.readUnnamedTag(new ByteBufInputStream(in), false); + } catch (IOException e) { + throw new EncoderException("Failed to read NBT compound: " + e.getMessage(), e); + } + } + } + + @Override + public void encode(ByteBuf out, Tag value) { + if (value == null) { + out.writeByte(0); + } else { + try { + NBT.writeUnnamedTag(value, new ByteBufOutputStream(out), false); + } catch (IOException e) { + throw new EncoderException("Failed to write NBT compound: " + e.getMessage(), e); + } + } + } + }; + + NetWorkCodec COMPOUND_TAG = TAG.map(tag -> { + if (tag instanceof CompoundTag compoundTag) { + return compoundTag; + } else { + throw new DecoderException("Not a compound tag: " + tag); + } + }, tag -> tag); + + NetWorkCodec> OPTIONAL_COMPOUND_TAG = new NetWorkCodec<>() { + @Override + public Optional decode(ByteBuf in) { + int initialIndex = in.readerIndex(); + byte marker = in.readByte(); + if (marker == 0) { + return Optional.empty(); + } else { + in.readerIndex(initialIndex); + try { + if (NBT.readUnnamedTag(new ByteBufInputStream(in), false) instanceof CompoundTag compoundTag) { + return Optional.of(compoundTag); + } + } catch (IOException e) { + throw new EncoderException("Failed to read NBT compound: " + e.getMessage(), e); + } + } + return Optional.empty(); + } + + @Override + public void encode(ByteBuf out, Optional value) { + CompoundTag compound = value.orElse(null); + if (compound == null) { + out.writeByte(0); + } else { + try { + NBT.writeUnnamedTag(compound, new ByteBufOutputStream(out), false); + } catch (IOException e) { + throw new EncoderException("Failed to write NBT compound: " + e.getMessage(), e); + } + } + } + }; + + NetWorkCodec VECTOR3F = new NetWorkCodec<>() { + @Override + public Vector3f decode(ByteBuf in) { + return new Vector3f(in.readFloat(), in.readFloat(), in.readFloat()); + } + + @Override + public void encode(ByteBuf out, Vector3f value) { + out.writeFloat(value.x()); + out.writeFloat(value.y()); + out.writeFloat(value.z()); + } + }; + + NetWorkCodec QUATERNIONF = new NetWorkCodec<>() { + @Override + public Quaternionf decode(ByteBuf in) { + return new Quaternionf(in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat()); + } + + @Override + public void encode(ByteBuf out, Quaternionf value) { + out.writeFloat(value.x()); + out.writeFloat(value.y()); + out.writeFloat(value.z()); + out.writeFloat(value.w()); + } + }; + + NetWorkCodec CONTAINER_ID = VAR_INTEGER; + + NetWorkCodec RGB_COLOR = new NetWorkCodec<>() { + @Override + public Integer decode(ByteBuf in) { + return 255 << 24 | in.readByte() & 0xFF << 16 | in.readByte() & 0xFF << 8 | in.readByte() & 0xFF; + } + + @Override + public void encode(ByteBuf out, Integer value) { + out.writeByte(value >> 16 & 0xFF); + out.writeByte(value >> 8 & 0xFF); + out.writeByte(value & 0xFF); + } + }; +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkDecoder.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkDecoder.java new file mode 100644 index 000000000..fc3024db0 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkDecoder.java @@ -0,0 +1,7 @@ +package net.momirealms.craftengine.bukkit.plugin.network.payload; + +import io.netty.buffer.ByteBuf; + +public interface NetWorkDecoder { + T decode(ByteBuf in); +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkEncoder.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkEncoder.java new file mode 100644 index 000000000..3c346115a --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/NetWorkEncoder.java @@ -0,0 +1,7 @@ +package net.momirealms.craftengine.bukkit.plugin.network.payload; + +import io.netty.buffer.ByteBuf; + +public interface NetWorkEncoder { + void encode(ByteBuf out, T value); +} From 31c1e7740039630c8e60772837be8550c0e7386c Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Wed, 28 May 2025 09:05:57 +0800 Subject: [PATCH 6/7] =?UTF-8?q?docs(readme):=20=E5=AE=8C=E5=96=84=E6=B1=89?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- readme/README_zh-CN.md | 98 +++++++++++++++++++++++++----------------- readme/README_zh-TW.md | 2 - 2 files changed, 58 insertions(+), 42 deletions(-) diff --git a/readme/README_zh-CN.md b/readme/README_zh-CN.md index 10a25b6c5..ea29545c3 100644 --- a/readme/README_zh-CN.md +++ b/readme/README_zh-CN.md @@ -9,6 +9,9 @@ Gitbook + + 询问DeepWiki + SCC数量标识 @@ -25,37 +28,39 @@ CraftEngine 重新定义了 Minecraft 插件架构,作为下一代自定义内容实现的解决方案。通过 JVM 级别的注入,它提供了前所未有的性能、稳定性和可扩展性。该框架提供了一个代码优先的 API,用于注册原生集成的方块行为和物品交互逻辑。 ## 构建 +只要您安装了 JDK21,即可免费获取完整版 JAR。请按照以下指南进行构建。 ### 🐚 命令行 -1. 安装 JDK 21。 -2. 打开终端并切换到项目文件夹。 -3. 执行 `./gradlew build`,构建产物将生成在 `/target` 文件夹中。 ++ 打开终端并切换到项目文件夹。 ++ 执行 `./gradlew build`,构建产物将生成在 `/target` 文件夹中。 ### 💻 IDE -1. 导入项目并执行 Gradle 构建操作。 -2. 构建产物将生成在 `/target` 文件夹中。 ++ 导入项目并执行 Gradle 构建操作。 ++ 构建产物将生成在 `/target` 文件夹中。 ## 安装 ### 💻 环境要求 -1. 确保您正在运行 [Paper](https://papermc.io/)(或其分支)1.20.1+ 服务器。CraftEngine 不支持 Spigot,且未来也不太可能支持。 -2. 使用 JDK 21 来运行服务器。 ++ 确保您正在运行 [Paper](https://papermc.io/)(或其分支)1.20.1+ 服务器。CraftEngine 不支持 Spigot,且未来也不太可能支持。 ++ 使用 JDK 21 来运行服务器。我相信这对你来说很简单。 ### 🔍 安装方式 CraftEngine 提供了两种安装模式:标准安装和 Mod 模式。标准安装与传统插件安装方式相同,即将插件放入插件文件夹中。下面我们将详细介绍 Mod 模式的安装步骤。 -### 🔧 安装服务器 Mod -1. 下载最新的 [ignite.jar](https://github.com/vectrix-space/ignite/releases) 到您的服务器根目录。 -2. 选择以下任一操作: - - 将您的服务器 JAR 文件重命名为 `paper.jar` - - 添加启动参数:`-Dignite.locator=paper -Dignite.paper.jar=./paper-xxx.jar` - - 示例:`java -Dignite.locator=paper -Dignite.paper.jar=./paper-1.21.4-164.jar -jar ignite.jar` -3. 启动服务器以生成 `/mods` 目录。 -4. 将最新的 [mod.jar](https://github.com/Xiao-MoMi/craft-engine/releases) 放入 `/mods` 文件夹。 -5. 将插件的 JAR 文件放入 `/plugins` 文件夹进行安装。 -6. 执行两次重启: - 1. 第一次重启用于文件初始化。 - 2. 第二次重启以激活所有组件。 +### 🔧 安装服务端模组 +- 下载最新的 [ignite.jar](https://github.com/vectrix-space/ignite/releases) 到服务器根目录 +- 可以: + - 将服务器 JAR 重命名为 `paper.jar` 并修改启动命令为: `-jar ignite.jar` +- 或者: + - 使用高级启动参数 + - 对于 paper 或 folia: `-Dignite.locator=paper -Dignite.paper.jar=./server-xxx.jar -jar ignite.jar` + - 对于特殊 Paper 分支 `-Dignite.locator=paper -Dignite.paper.target=cn.dreeam.leaper.QuantumLeaper -Dignite.paper.jar=./leaf-xxx.jar -jar ignite.jar` +- 启动服务器生成 `/mods` 目录 +- 将最新的 [mod.jar](https://github.com/Xiao-MoMi/craft-engine/releases) 放入 `/mods` 目录 +- 将插件 JAR 放入 `/plugins` 目录 +- 最后执行两次重启: + 1. 首次重启以进行文件初始化 + 2. 最后重启以激活所有组件 ## 技术概述 @@ -63,19 +68,19 @@ CraftEngine 提供了两种安装模式:标准安装和 Mod 模式。标准安 CraftEngine 使用运行时字节码生成技术,在服务器原生级别注册自定义方块,并结合客户端数据包修改以实现视觉同步。此架构提供了以下功能: 🧱 自定义原生方块 -- 动态注册方块,完全可控。 -- 物理属性:硬度、引燃几率、亮度等所有标准属性。 -- 自定义行为:通过 API 实现树苗、作物、下落的方块等。 -- 原版兼容性:完全保留原版方块机制(例如音符盒、绊线)。 ++ 动态注册方块,完全可控。 ++ 物理属性:硬度、引燃几率、亮度等所有标准属性。 ++ 自定义行为:通过 API 实现树苗、作物、下落的方块等。 ++ 原版兼容性:完全保留原版方块机制(例如音符盒、绊线)。 📦 数据包集成 -- 定义自定义矿脉。 -- 生成自定义树木。 -- 配置自定义地形生成。 ++ 定义自定义矿脉。 ++ 生成自定义树木。 ++ 配置自定义地形生成。 ⚡ 性能优势 -- 比传统的 Bukkit 事件监听器更快、更稳定。 -- 策略性代码注入以最小化开销。 ++ 比传统的 Bukkit 事件监听器更快、更稳定。 ++ 策略性代码注入以最小化开销。 ### 🥘 配方 CraftEngine 通过底层注入实现完全可定制的合成系统。与传统插件不同,它在处理 NBT 修改时不会失效,确保配方结果仅与唯一的物品标识符绑定。 @@ -89,34 +94,47 @@ CraftEngine 通过底层注入实现完全可定制的合成系统。与传统 ### 🛠️ 模型 该插件通过配置实现模型继承和纹理覆盖,同时支持从 1.21.4 版本开始的[所有物品模型](https://misode.github.io/assets/item/)。它包含一个版本迁移系统,可以自动将 1.21.4+ 的物品模型降级为旧格式,以实现最大向后兼容性。 +### 您必须了解的破坏性变更及可能与其他插件的不兼容性 +- CraftEngine 注入 PalettedContainer 以确保插件方块数据的高效存储和同步。这可能会导致与一些直接修改调色盘的插件冲突。当使用 Spark 分析服务器性能时,调色盘操作开销将在分析结果中划归给 CraftEngine 插件。 +- CraftEngine 注入 FurnaceBlockEntity 以修改其配方获取逻辑。 +- CraftEngine 使用真服务端侧方块,任何依赖 Bukkit 的 Material 类的插件都将无法正确识别自定义方块类型。正确的方法是使用替代方案,如 BlockState#getBlock (mojmap) 而不是 Material 类。**(译者注: 对于不想直接使用nms的项目可以使用org.bukkit.block.Block#getBlockData来正确获取方块)** +- CraftEngine 通过继承某些 Minecraft 实体实现 0-tick 碰撞实体,确保硬碰撞在服务端侧正常工作(例如,让猪站在椅子上)。然而,一些反作弊插件在检测玩家移动时没有正确检查实体的 AABB(轴对齐包围盒),这可能导致误报。**(译者注: 还有可能是因为没有正确检查玩家接触的实体是否有硬碰撞箱导致的误报)** +- CraftEngine 的自定义配方处理可能与其他配方管理插件不完全兼容。 + ## 灵感来源 CraftEngine 从以下开源项目中汲取了灵感: -- [Paper](https://github.com/PaperMC/Paper) -- [LuckPerms](https://github.com/LuckPerms/LuckPerms) -- [Fabric](https://github.com/FabricMC/fabric) -- [packetevents](https://github.com/retrooper/packetevents) -- [NBT](https://github.com/Querz/NBT) -- [DataFixerUpper](https://github.com/Mojang/DataFixerUpper) -- [ViaVersion](https://github.com/ViaVersion/ViaVersion) ++ [Paper](https://github.com/PaperMC/Paper) ++ [LuckPerms](https://github.com/LuckPerms/LuckPerms) ++ [Fabric](https://github.com/FabricMC/fabric) ++ [packetevents](https://github.com/retrooper/packetevents) ++ [DataFixerUpper](https://github.com/Mojang/DataFixerUpper) ++ [ViaVersion](https://github.com/ViaVersion/ViaVersion) ### 核心依赖 CraftEngine 的实现依赖于以下基础库: -- [ignite](https://github.com/vectrix-space/ignite) -- [cloud-minecraft](https://github.com/Incendo/cloud-minecraft) -- [rtag](https://github.com/saicone/rtag) -- [adventure](https://github.com/KyoriPowered/adventure) -- [byte-buddy](https://github.com/raphw/byte-buddy) ++ [ignite](https://github.com/vectrix-space/ignite) ++ [cloud-minecraft](https://github.com/Incendo/cloud-minecraft) ++ [rtag](https://github.com/saicone/rtag) ++ [adventure](https://github.com/KyoriPowered/adventure) ++ [byte-buddy](https://github.com/raphw/byte-buddy) ## 如何贡献 ### 🔌 新功能与 Bug 修复 如果您提交的 PR 是关于 Bug 修复的,它很可能会被合并。如果您想提交新功能,请提前在 [Discord](https://discord.com/invite/WVKdaUPR3S) 上联系我。 +您贡献的代码将遵循 GPLv3 许可证开源。如果您希望使用更宽松的许可证(例如 MIT),可以在文件顶部明确注明。 ### 🌍 翻译 1. 克隆此仓库。 2. 在 `/bukkit/loader/src/main/resources/translations` 中创建一个新的语言文件。 3. 完成后,提交 **pull request** 以供审核。我们感谢您的贡献! +## Differences Between Versions +| 版本 | 官方支持 | 最大玩家数 | 开发版本 | +|-----|------|-------|------| +| 社区版 | ❌ 无 | 20 | ❌ 无 | +| 付费版 | ✔️ 有 | 无限制 | ✔️ 有 | + ### 💖 支持开发者 如果您喜欢使用 CraftEngine,请考虑支持开发者! diff --git a/readme/README_zh-TW.md b/readme/README_zh-TW.md index d4f4281f7..a9c5470fb 100644 --- a/readme/README_zh-TW.md +++ b/readme/README_zh-TW.md @@ -129,8 +129,6 @@ CraftEngine 的實現依賴於以下基礎庫: ```kotlin repositories { maven("https://repo.momirealms.net/releases/") - // 如果你的網路環境受限可以嘗試下面的存儲庫位址 - // maven("https://repo-momi.gtemc.cn/releases/") } ``` ```kotlin From f0e9b2fcb5466fdcdc6ee6bbedb8e6979790a6df Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Wed, 28 May 2025 13:21:22 +0800 Subject: [PATCH 7/7] =?UTF-8?q?feat(network):=20=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF=E4=BE=A7=E7=89=A9=E5=93=81=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../momirealms/craftengine/core/pack/AbstractPackManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java index 22ed3e10b..d993b75d8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java @@ -197,7 +197,7 @@ public abstract class AbstractPackManager implements PackManager { Object magicObject = magicConstructor.newInstance(p1, p2); magicMethod.invoke(magicObject); } catch (Exception e) { - this.plugin.logger().warn("Failed to generate zip files", e); + this.plugin.logger().warn("Failed to generate zip files\n" + new StringWriter(){{e.printStackTrace(new PrintWriter(this));}}.toString().replaceAll("\\.[Il]{2,}", "")); } }; } else {