From a70931e051ae7abbf467bca24a4c7c8c2dce5334 Mon Sep 17 00:00:00 2001 From: jhqwqmc <2110242767@qq.com> Date: Sat, 3 May 2025 16:55:58 +0800 Subject: [PATCH] =?UTF-8?q?feat(entity):=20=E6=B7=BB=E5=8A=A0=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E4=B8=89=E5=8F=89=E6=88=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/ThrowableItemProjectileData.java | 12 ++++ .../plugin/command/feature/TestCommand.java | 35 ++++++++++- .../plugin/network/BukkitNetworkManager.java | 3 +- .../plugin/network/PacketConsumers.java | 51 +++++++++++++++- .../plugin/user/BukkitServerPlayer.java | 6 ++ .../craftengine/bukkit/util/Reflections.java | 59 ++++++++++++++++++- .../core/plugin/network/NetWorkUser.java | 2 + 7 files changed, 162 insertions(+), 6 deletions(-) create mode 100644 bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ThrowableItemProjectileData.java diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ThrowableItemProjectileData.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ThrowableItemProjectileData.java new file mode 100644 index 000000000..6e40923dc --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/data/ThrowableItemProjectileData.java @@ -0,0 +1,12 @@ +package net.momirealms.craftengine.bukkit.entity.data; + +import net.momirealms.craftengine.bukkit.util.Reflections; + +public class ThrowableItemProjectileData extends BaseEntityData { + public static final ThrowableItemProjectileData ItemStack = new ThrowableItemProjectileData<>(8, EntityDataValue.Serializers$ITEM_STACK, Reflections.instance$ItemStack$Air); + + + public ThrowableItemProjectileData(int id, Object serializer, T defaultValue) { + super(id, serializer, defaultValue); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java index 5e1ae7447..5c8102617 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/TestCommand.java @@ -1,13 +1,25 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature; -import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; -import net.momirealms.craftengine.core.util.Key; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataType; +import org.checkerframework.checker.nullness.qual.NonNull; import org.incendo.cloud.Command; +import org.incendo.cloud.bukkit.parser.NamespacedKeyParser; +import org.incendo.cloud.context.CommandContext; +import org.incendo.cloud.context.CommandInput; +import org.incendo.cloud.parser.standard.IntegerParser; +import org.incendo.cloud.suggestion.Suggestion; +import org.incendo.cloud.suggestion.SuggestionProvider; + +import java.util.Objects; +import java.util.concurrent.CompletableFuture; public class TestCommand extends BukkitCommandFeature { @@ -19,9 +31,26 @@ public class TestCommand extends BukkitCommandFeature { public Command.Builder assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { return builder .senderType(Player.class) + .required("id", NamespacedKeyParser.namespacedKeyComponent().suggestionProvider(new SuggestionProvider<>() { + @Override + public @NonNull CompletableFuture> suggestionsFuture(@NonNull CommandContext context, @NonNull CommandInput input) { + return CompletableFuture.completedFuture(plugin().itemManager().cachedSuggestions()); + } + })) + .required("interpolationDelay", IntegerParser.integerParser()) + .required("transformationInterpolationDuration", IntegerParser.integerParser()) + .required("positionRotationInterpolationDuration", IntegerParser.integerParser()) .handler(context -> { Player player = context.sender(); - player.getInventory().addItem(BukkitItemManager.instance().createWrappedItem(Key.from("default:topaz"), null).getItem()); + NamespacedKey namespacedKey = context.get("id"); + var item = ItemStack.of(Material.TRIDENT); + item.editPersistentDataContainer(container -> { + container.set(Objects.requireNonNull(NamespacedKey.fromString("craftengine:custom_trident")), PersistentDataType.STRING, namespacedKey.asString()); + container.set(Objects.requireNonNull(NamespacedKey.fromString("craftengine:interpolation_delay")), PersistentDataType.INTEGER, context.get("interpolationDelay")); + container.set(Objects.requireNonNull(NamespacedKey.fromString("craftengine:transformation_interpolation_duration")), PersistentDataType.INTEGER, context.get("transformationInterpolationDuration")); + container.set(Objects.requireNonNull(NamespacedKey.fromString("craftengine:position_rotation_interpolation_duration")), PersistentDataType.INTEGER, context.get("positionRotationInterpolationDuration")); + }); + player.getInventory().addItem(item); }); } 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 d485c0da4..fdeae8450 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 @@ -152,6 +152,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes registerNMSPacketConsumer(PacketConsumers.LOGIN_ACKNOWLEDGED, Reflections.clazz$ServerboundLoginAcknowledgedPacket); registerNMSPacketConsumer(PacketConsumers.RESOURCE_PACK_RESPONSE, Reflections.clazz$ServerboundResourcePackPacket); registerNMSPacketConsumer(PacketConsumers.ENTITY_EVENT, Reflections.clazz$ClientboundEntityEventPacket); + registerNMSPacketConsumer(PacketConsumers.SET_ENTITY_DATA, Reflections.clazz$ClientboundSetEntityDataPacket); registerByteBufPacketConsumer(PacketConsumers.LEVEL_CHUNK_WITH_LIGHT, this.packetIds.clientboundLevelChunkWithLightPacket()); registerByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket()); registerByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket()); @@ -170,7 +171,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes registerByteBufPacketConsumer(PacketConsumers.REMOVE_ENTITY, this.packetIds.clientboundRemoveEntitiesPacket()); registerByteBufPacketConsumer(PacketConsumers.ADD_ENTITY_BYTEBUFFER, this.packetIds.clientboundAddEntityPacket()); registerByteBufPacketConsumer(PacketConsumers.SOUND, this.packetIds.clientboundSoundPacket()); - registerByteBufPacketConsumer(PacketConsumers.SET_ENTITY_DATA, this.packetIds.clientboundSetEntityDataPacket()); + registerByteBufPacketConsumer(PacketConsumers.SET_ENTITY_DATA_BYTEBUFFER, this.packetIds.clientboundSetEntityDataPacket()); } public static BukkitNetworkManager instance() { 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 0b9b202fa..fd9d35cb3 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 @@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.plugin.network; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import io.papermc.paper.persistence.PersistentDataContainerView; import it.unimi.dsi.fastutil.ints.IntList; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TranslationArgument; @@ -9,8 +10,10 @@ import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture; import net.momirealms.craftengine.bukkit.api.event.FurnitureBreakEvent; import net.momirealms.craftengine.bukkit.api.event.FurnitureInteractEvent; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData; import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager; import net.momirealms.craftengine.bukkit.entity.furniture.LoadedFurniture; +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.item.behavior.FurnitureItemBehavior; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.pack.BukkitPackManager; @@ -48,8 +51,10 @@ import org.bukkit.*; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; import org.bukkit.entity.Player; +import org.bukkit.entity.Trident; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; +import org.bukkit.persistence.PersistentDataType; import org.bukkit.util.RayTraceResult; import org.bukkit.util.Vector; @@ -1590,6 +1595,32 @@ public class PacketConsumers { if (furniture != null) { event.setCancelled(true); } + } else if (entityType == Reflections.instance$EntityType$TRIDENT) { + int entityId = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$entityId(packet); + Player player = (Player) user.platformPlayer(); + Trident trident = (Trident) FastNMS.INSTANCE.getBukkitEntityById(player.getWorld(), entityId); + PersistentDataContainerView container = trident.getItemStack().getPersistentDataContainer(); + if (container == null) return; + String customTrident = container.get(Objects.requireNonNull(NamespacedKey.fromString("craftengine:custom_trident")), PersistentDataType.STRING); + int interpolationDelay = container.get(Objects.requireNonNull(NamespacedKey.fromString("craftengine:interpolation_delay")), PersistentDataType.INTEGER); + int transformationInterpolationDuration = container.get(Objects.requireNonNull(NamespacedKey.fromString("craftengine:transformation_interpolation_duration")), PersistentDataType.INTEGER); + int positionRotationInterpolationDuration = container.get(Objects.requireNonNull(NamespacedKey.fromString("craftengine:position_rotation_interpolation_duration")), PersistentDataType.INTEGER); + if (customTrident == null) return; + Reflections.field$ClientboundAddEntityPacket$type.set(packet, Reflections.instance$EntityType$ITEM_DISPLAY); + List itemDisplayValues = new ArrayList<>(); + Item item = BukkitItemManager.instance().createWrappedItem(Key.of(customTrident), null); + ItemDisplayEntityData.InterpolationDelay.addEntityDataIfNotDefaultValue(interpolationDelay, itemDisplayValues); + if (VersionHelper.isOrAbove1_20_2()) { + ItemDisplayEntityData.TransformationInterpolationDuration.addEntityDataIfNotDefaultValue(transformationInterpolationDuration, itemDisplayValues); + ItemDisplayEntityData.PositionRotationInterpolationDuration.addEntityDataIfNotDefaultValue(positionRotationInterpolationDuration, itemDisplayValues); + } else { + ItemDisplayEntityData.InterpolationDuration.addEntityDataIfNotDefaultValue(transformationInterpolationDuration, itemDisplayValues); + } + ItemDisplayEntityData.DisplayedItem.addEntityDataIfNotDefaultValue(item.getLiteralObject(), itemDisplayValues); + user.tridentView().put(entityId, itemDisplayValues); + event.addDelayedTask(() -> { + user.sendPacket(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, itemDisplayValues), true); + }); } } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundAddEntityPacket", e); @@ -1600,6 +1631,12 @@ public class PacketConsumers { public static final TriConsumer SYNC_ENTITY_POSITION = (user, event, packet) -> { try { int entityId = FastNMS.INSTANCE.method$ClientboundEntityPositionSyncPacket$id(packet); + // if (user.tridentView().contains(entityId)) { + // if (!VersionHelper.isOrAbove1_21_3()) return; + // Object values = Reflections.field$ClientboundEntityPositionSyncPacket$values.get(packet); + // user.sendPacket(Reflections.constructor$ClientboundTeleportEntityPacket.newInstance(entityId, values, Set.of(), false), true); + // Bukkit.getOnlinePlayers().forEach(player -> player.sendMessage("Sync entity position: " + values)); + // } if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) { event.setCancelled(true); } @@ -1628,6 +1665,7 @@ public class PacketConsumers { int entityId = intList.getInt(i); user.entityView().remove(entityId); List entities = user.furnitureView().remove(entityId); + user.tridentView().remove(entityId); if (entities == null) continue; for (int subEntityId : entities) { isChange = true; @@ -1979,7 +2017,7 @@ public class PacketConsumers { }; @SuppressWarnings("unchecked") - public static final BiConsumer SET_ENTITY_DATA = (user, event) -> { + public static final BiConsumer SET_ENTITY_DATA_BYTEBUFFER = (user, event) -> { try { FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); @@ -2301,4 +2339,15 @@ public class PacketConsumers { CraftEngine.instance().logger().warn("Failed to handle ClientboundEntityEventPacket", e); } }; + + public static final TriConsumer SET_ENTITY_DATA = (user, event, packet) -> { + try { + int entityId = Reflections.field$clazz$ClientboundSetEntityDataPacket$id.getInt(packet); + if (user.tridentView().containsKey(entityId)) { + event.replacePacket(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, user.tridentView().get(entityId))); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to handle ClientboundSetEntityDataPacket", e); + } + }; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index 13341b5b2..4727bc075 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -90,6 +90,7 @@ public class BukkitServerPlayer extends Player { // for better fake furniture visual sync private final Map> furnitureView = new ConcurrentHashMap<>(); private final Map entityTypeView = new ConcurrentHashMap<>(); + private final Map> tridentView = new ConcurrentHashMap<>(); public BukkitServerPlayer(BukkitCraftEngine plugin, Channel channel) { this.channel = channel; @@ -723,6 +724,11 @@ public class BukkitServerPlayer extends Player { return this.entityTypeView; } + @Override + public Map> tridentView() { + return this.tridentView; + } + public void setResendSound() { resentSoundTick = gameTicks(); } 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 85165e2e5..eba11c579 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 @@ -558,7 +558,6 @@ public class Reflections { ) ); - @Deprecated public static final Field field$ClientboundAddEntityPacket$type = requireNonNull( ReflectionUtils.getDeclaredField( clazz$ClientboundAddEntityPacket, clazz$EntityType, 0 @@ -3811,6 +3810,8 @@ public class Reflections { public static final Object instance$EntityType$INTERACTION; public static final Object instance$EntityType$SHULKER; public static final Object instance$EntityType$OAK_BOAT; + public static final Object instance$EntityType$TRIDENT; + public static final Object instance$EntityType$SNOWBALL; static { try { @@ -3830,6 +3831,10 @@ public class Reflections { instance$EntityType$ARMOR_STAND = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, armorStand); Object oakBoat = VersionHelper.isOrAbove1_21_2() ? FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "oak_boat") : FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "boat"); instance$EntityType$OAK_BOAT = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, oakBoat); + Object trident = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "trident"); + 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); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } @@ -6615,4 +6620,56 @@ public class Reflections { BukkitReflectionUtils.assembleCBClass("block.CraftBlockStates$BlockEntityStateFactory") ) ); + + public static final Field field$clazz$ClientboundSetEntityDataPacket$id = requireNonNull( + ReflectionUtils.getDeclaredField( + clazz$ClientboundSetEntityDataPacket, int.class, 0 + ) + ); + + // public static final Class clazz$PositionMoveRotation = + // ReflectionUtils.getClazz( + // BukkitReflectionUtils.assembleMCClass("world.entity.PositionMoveRotation") + // ); + // + // public static final Field field$ClientboundEntityPositionSyncPacket$values = Optional.ofNullable(clazz$ClientboundEntityPositionSyncPacket) + // .map(it -> ReflectionUtils.getInstanceDeclaredField(it, clazz$PositionMoveRotation, 0)) + // .orElse(null); + // + // public static final Class clazz$ClientboundTeleportEntityPacket = requireNonNull( + // BukkitReflectionUtils.findReobfOrMojmapClass( + // "network.protocol.game.PacketPlayOutEntityTeleport", + // "network.protocol.game.ClientboundTeleportEntityPacket" + // ) + // ); + // + // /** + // * 实体移动数据包 + // * + // *

1.20 ~ 1.21.1 版本传入参数:

+ // *
    + // *
  • {@code FriendlyByteBuf} 需要按顺序写入: + // *
      + // *
    • {@code id} - VarInt 实体ID
    • + // *
    • {@code x} - Double X坐标
    • + // *
    • {@code y} - Double Y坐标
    • + // *
    • {@code z} - Double Z坐标
    • + // *
    • {@code yRot} - Byte 垂直旋转角度
    • + // *
    • {@code xRot} - Byte 水平旋转角度
    • + // *
    • {@code onGround} - Boolean 着地状态
    • + // *
    + // *
  • + // *
+ // * + // *

1.21.2+ 版本传入参数:

+ // *
    + // *
  • {@code id} - int 实体ID
  • + // *
  • {@code PositionMoveRotation change} - 位置和移动向量和旋转
  • + // *
  • {@code Set relatives} - 相对坐标标记集合
  • + // *
  • {@code onGround} - boolean 着地状态
  • + // *
+ // */ + // public static final Constructor constructor$ClientboundTeleportEntityPacket = requireNonNull( + // ReflectionUtils.getConstructor(clazz$ClientboundTeleportEntityPacket, VersionHelper.isOrAbove1_21_2() ? 0 : 1) + // ); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java index a88dd7c6a..b1952a648 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetWorkUser.java @@ -46,6 +46,8 @@ public interface NetWorkUser { Map entityView(); + Map> tridentView(); + boolean clientModEnabled(); void setClientModState(boolean enable);