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/item/factory/ComponentItemFactory1_20_5.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java index 08cc98ec3..f7b5e76ff 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/ComponentItemFactory1_20_5.java @@ -309,7 +309,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory map = EnchantmentUtils.toMap(enchant); - map.put(enchantment.toString(), enchantment.level()); + map.put(enchantment.id().toString(), enchantment.level()); item.setJavaComponent(ComponentTypes.ENCHANTMENTS, map); } catch (ReflectiveOperationException e) { plugin.logger().warn("Failed to add enchantment", e); @@ -321,7 +321,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory map = EnchantmentUtils.toMap(enchant); - map.put(enchantment.toString(), enchantment.level()); + map.put(enchantment.id().toString(), enchantment.level()); item.setJavaComponent(ComponentTypes.STORED_ENCHANTMENTS, map); } catch (ReflectiveOperationException e) { plugin.logger().warn("Failed to add stored enchantment", e); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/UniversalItemFactory.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/UniversalItemFactory.java index 787dba986..3dd72fe51 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/UniversalItemFactory.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/factory/UniversalItemFactory.java @@ -198,9 +198,9 @@ public class UniversalItemFactory extends BukkitItemFactory { return; } } - item.add(Map.of("id", enchantment.toString(), "lvl", (short) enchantment.level()), "Enchantments"); + item.add(Map.of("id", enchantment.id().toString(), "lvl", (short) enchantment.level()), "Enchantments"); } else { - item.set(List.of(Map.of("id", enchantment.toString(), "lvl", (short) enchantment.level())), "Enchantments"); + item.set(List.of(Map.of("id", enchantment.id().toString(), "lvl", (short) enchantment.level())), "Enchantments"); } } @@ -214,9 +214,9 @@ public class UniversalItemFactory extends BukkitItemFactory { return; } } - item.add(Map.of("id", enchantment.toString(), "lvl", (short) enchantment.level()), "StoredEnchantments"); + item.add(Map.of("id", enchantment.id().toString(), "lvl", (short) enchantment.level()), "StoredEnchantments"); } else { - item.set(List.of(Map.of("id", enchantment.toString(), "lvl", (short) enchantment.level())), "StoredEnchantments"); + item.set(List.of(Map.of("id", enchantment.id().toString(), "lvl", (short) enchantment.level())), "StoredEnchantments"); } } 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 116ac584a..d9a512893 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,15 +1,35 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature; +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature; +import net.momirealms.craftengine.bukkit.util.CustomTridentUtils; +import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager; -import net.momirealms.craftengine.core.plugin.context.ContextHolder; -import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; -import net.momirealms.craftengine.core.util.AdventureHelper; +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.entity.Trident; +import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; +import org.checkerframework.checker.nullness.qual.NonNull; import org.incendo.cloud.Command; -import org.incendo.cloud.parser.standard.StringParser; +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.*; +import org.incendo.cloud.suggestion.Suggestion; +import org.incendo.cloud.suggestion.SuggestionProvider; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; public class TestCommand extends BukkitCommandFeature { @@ -18,14 +38,29 @@ public class TestCommand extends BukkitCommandFeature { } @Override + @SuppressWarnings("deprecation") public Command.Builder assembleCommand(org.incendo.cloud.CommandManager manager, Command.Builder builder) { return builder .senderType(Player.class) - .required("text", StringParser.greedyStringParser()) + .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("displayType", ByteParser.byteParser((byte) 0, (byte) 8)) + .required("translation", StringParser.stringParser()) + .required("rotationLeft", StringParser.stringParser()) .handler(context -> { - String text = ""; - PlayerOptionalContext context1 = PlayerOptionalContext.of(plugin().adapt(context.sender()), ContextHolder.builder()); - plugin().senderFactory().wrap(context.sender()).sendMessage(AdventureHelper.customMiniMessage().deserialize(text, context1.tagResolvers())); + Player player = context.sender(); + NamespacedKey namespacedKey = context.get("id"); + ItemStack item = new ItemStack(Material.TRIDENT); + item.editMeta((meta) -> { + Item ceItem = BukkitItemManager.instance().createWrappedItem(Key.of(namespacedKey.asString()), null); + Optional customModelData = ceItem.customModelData(); + customModelData.ifPresent(meta::setCustomModelData); + }); + 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 122246cb5..95bca0475 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.MOVE_ENTITY_ALL, Reflections.clazz$ClientboundMoveEntityPacket$PosRot); registerByteBufPacketConsumer(PacketConsumers.LEVEL_CHUNK_WITH_LIGHT, this.packetIds.clientboundLevelChunkWithLightPacket()); registerByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket()); registerByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket()); 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 b91cbd887..183b20b31 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 @@ -35,6 +35,7 @@ import net.momirealms.craftengine.core.plugin.network.ConnectionState; import net.momirealms.craftengine.core.plugin.network.NetWorkUser; import net.momirealms.craftengine.core.plugin.network.NetworkManager; import net.momirealms.craftengine.core.plugin.network.ProtocolVersion; +import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask; import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.world.BlockHitResult; import net.momirealms.craftengine.core.world.BlockPos; @@ -1607,16 +1608,22 @@ public class PacketConsumers { if (furniture != null) { event.setCancelled(true); } + } else if (entityType == Reflections.instance$EntityType$TRIDENT) { + CustomTridentUtils.handleCustomTrident(user, event, packet); } } catch (Exception e) { CraftEngine.instance().logger().warn("Failed to handle ClientboundAddEntityPacket", e); } }; - // 1.21.3+ + // 1.21.2+ public static final TriConsumer SYNC_ENTITY_POSITION = (user, event, packet) -> { try { int entityId = FastNMS.INSTANCE.method$ClientboundEntityPositionSyncPacket$id(packet); + if (user.tridentView().containsKey(entityId)) { + event.replacePacket(CustomTridentUtils.buildCustomTridentPositionSync(packet, entityId)); + return; + } if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) { event.setCancelled(true); } @@ -1645,6 +1652,11 @@ public class PacketConsumers { int entityId = intList.getInt(i); user.entityView().remove(entityId); List entities = user.furnitureView().remove(entityId); + SchedulerTask task = user.tridentTaskView().remove(entityId); + if (task != null) { + task.cancel(); + } + user.tridentView().remove(entityId); if (entities == null) continue; for (int subEntityId : entities) { isChange = true; @@ -2000,6 +2012,16 @@ public class PacketConsumers { try { FriendlyByteBuf buf = event.getBuffer(); int id = buf.readVarInt(); + if (user.tridentView().containsKey(id)) { + List packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf); + List newPackedItems = CustomTridentUtils.buildCustomTridentSetEntityDataPacket(user, packedItems, id); + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeVarInt(id); + FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(newPackedItems, buf); + return; + } Object entityType = user.entityView().get(id); if (entityType == Reflections.instance$EntityType$BLOCK_DISPLAY) { boolean isChanged = false; @@ -2318,4 +2340,15 @@ public class PacketConsumers { CraftEngine.instance().logger().warn("Failed to handle ClientboundEntityEventPacket", e); } }; + + public static final TriConsumer MOVE_ENTITY_ALL = (user, event, packet) -> { + try { + int entityId = BukkitInjector.internalFieldAccessor().field$ClientboundMoveEntityPacket$entityId(packet); + if (user.tridentView().containsKey(entityId)) { + event.replacePacket(CustomTridentUtils.buildCustomTridentMove(packet, entityId)); + } + } catch (Exception e) { + CraftEngine.instance().logger().warn("Failed to handle ClientboundMoveEntityPacket$PosRot", 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 b72d8beef..a9e2d0eba 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 @@ -20,6 +20,7 @@ import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.network.ConnectionState; import net.momirealms.craftengine.core.plugin.network.ProtocolVersion; +import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask; import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.VersionHelper; @@ -93,6 +94,8 @@ 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<>(); + private final Map addTridentPacketView = new ConcurrentHashMap<>(); public BukkitServerPlayer(BukkitCraftEngine plugin, Channel channel) { this.channel = channel; @@ -737,6 +740,16 @@ public class BukkitServerPlayer extends Player { return this.entityTypeView; } + @Override + public Map> tridentView() { + return this.tridentView; + } + + @Override + public Map tridentTaskView() { + return this.addTridentPacketView; + } + public void setResendSound() { resentSoundTick = gameTicks(); } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/CustomTridentUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/CustomTridentUtils.java new file mode 100644 index 000000000..7f87849de --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/CustomTridentUtils.java @@ -0,0 +1,163 @@ +package net.momirealms.craftengine.bukkit.util; + +import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData; +import net.momirealms.craftengine.bukkit.item.BukkitItemManager; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.network.NMSPacketEvent; +import net.momirealms.craftengine.core.entity.CustomTrident; +import net.momirealms.craftengine.core.item.CustomItem; +import net.momirealms.craftengine.core.item.Enchantment; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.network.NetWorkUser; +import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.MCUtils; +import net.momirealms.craftengine.core.util.VersionHelper; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Trident; +import org.bukkit.inventory.ItemStack; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +public class CustomTridentUtils { + + public static void handleCustomTrident(NetWorkUser user, NMSPacketEvent event, Object packet) { + int entityId = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$entityId(packet); + Trident trident = getTridentById(user, entityId); + if (trident == null) return; + World world = trident.getWorld(); + Object nmsEntity = FastNMS.INSTANCE.method$CraftEntity$getHandle(trident); + Object trackedEntity = FastNMS.INSTANCE.field$Entity$trackedEntity(nmsEntity); + Object serverEntity = FastNMS.INSTANCE.filed$ChunkMap$TrackedEntity$serverEntity(trackedEntity); + if (notCustomTrident(trident)) return; + Object newPacket = modifyCustomTridentPacket(packet, entityId); + List itemDisplayValues = buildEntityDataValues(trident); + user.tridentView().put(entityId, itemDisplayValues); + user.sendPacket(newPacket, true); + user.sendPacket(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, itemDisplayValues), true); + event.setCancelled(true); + if (serverEntity != null) { + // 这里直接暴力更新 + SchedulerTask task = CraftEngine.instance().scheduler().asyncRepeating(() -> { + FastNMS.INSTANCE.method$ServerEntity$sendChanges(serverEntity); + if (canSpawnParticle(nmsEntity)) { + world.spawnParticle(ParticleUtils.getParticle("BUBBLE"), trident.getLocation(), 1, 0, 0, 0, 0); + } + }, 0, 5, TimeUnit.MILLISECONDS); + user.tridentTaskView().put(entityId, task); + } + } + + private static boolean canSpawnParticle(Object nmsEntity) { + if (!FastNMS.INSTANCE.field$AbstractArrow$wasTouchingWater(nmsEntity)) return false; + return !FastNMS.INSTANCE.method$AbstractArrow$isInGround(nmsEntity); + } + + @Nullable + public static Trident getTridentById(NetWorkUser user, int entityId) { + Player player = (Player) user.platformPlayer(); + Entity entity = FastNMS.INSTANCE.getBukkitEntityById(player.getWorld(), entityId); + if (entity instanceof Trident trident) return trident; + return null; + } + + public static boolean notCustomTrident(Trident trident) { + if (trident == null) return true; + Optional> customItem = BukkitItemManager.instance().wrap(trident.getItemStack()).getCustomItem(); + return customItem.map(itemStackCustomItem -> itemStackCustomItem.settings().customTrident() == null).orElse(true); + } + + public static Object modifyCustomTridentPacket(Object packet, int entityId) { + UUID uuid = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$uuid(packet); + double x = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$x(packet); + double y = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$y(packet); + double z = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$z(packet); + float yRot = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$yRot(packet); + float xRot = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$xRot(packet); + Object type = Reflections.instance$EntityType$ITEM_DISPLAY; + int data = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$data(packet); + double xa = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$xa(packet); + double ya = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$ya(packet); + double za = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$za(packet); + double yHeadRot = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$yHeadRot(packet); + return FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( + entityId, uuid, x, y, z, + MCUtils.clamp(-xRot, -90.0F, 90.0F), -yRot, + type, data, FastNMS.INSTANCE.constructor$Vec3(xa, ya, za), yHeadRot + ); + } + + public static List buildEntityDataValues(Trident trident) { + List itemDisplayValues = new ArrayList<>(); + ItemStack itemStack = trident.getItemStack(); + Optional> customItem = BukkitItemManager.instance().wrap(itemStack).getCustomItem(); + if (customItem.isEmpty()) return itemDisplayValues; + CustomTrident customTrident = customItem.get().settings().customTrident(); + Item item = BukkitItemManager.instance().createWrappedItem(customTrident.customTridentItemId(), null); + itemStack.getEnchantments().forEach((enchantment, level) -> item.addEnchantment(new Enchantment(Key.of(enchantment.getKey().toString()), level))); + ItemDisplayEntityData.InterpolationDelay.addEntityDataIfNotDefaultValue(-1, itemDisplayValues); + ItemDisplayEntityData.Translation.addEntityDataIfNotDefaultValue(customTrident.translation(), itemDisplayValues); + ItemDisplayEntityData.RotationLeft.addEntityDataIfNotDefaultValue(customTrident.rotationLefts(), itemDisplayValues); + if (VersionHelper.isOrAbove1_20_2()) { + ItemDisplayEntityData.TransformationInterpolationDuration.addEntityDataIfNotDefaultValue(2, itemDisplayValues); + ItemDisplayEntityData.PositionRotationInterpolationDuration.addEntityDataIfNotDefaultValue(2, itemDisplayValues); + } else { + ItemDisplayEntityData.InterpolationDuration.addEntityDataIfNotDefaultValue(2, itemDisplayValues); + } + ItemDisplayEntityData.DisplayedItem.addEntityDataIfNotDefaultValue(item.getLiteralObject(), itemDisplayValues); + ItemDisplayEntityData.DisplayType.addEntityDataIfNotDefaultValue(customTrident.displayType(), itemDisplayValues); + return itemDisplayValues; + } + + public static Object buildCustomTridentPositionSync(Object packet, int entityId) { + Object positionMoveRotation = FastNMS.INSTANCE.field$ClientboundEntityPositionSyncPacket$values(packet); + boolean onGround = FastNMS.INSTANCE.field$ClientboundEntityPositionSyncPacket$onGround(packet); + Object position = FastNMS.INSTANCE.field$PositionMoveRotation$position(positionMoveRotation); + Object deltaMovement = FastNMS.INSTANCE.field$PositionMoveRotation$deltaMovement(positionMoveRotation); + float yRot = FastNMS.INSTANCE.field$PositionMoveRotation$yRot(positionMoveRotation); + float xRot = FastNMS.INSTANCE.field$PositionMoveRotation$xRot(positionMoveRotation); + Object newPositionMoveRotation = FastNMS.INSTANCE.constructor$PositionMoveRotation(position, deltaMovement, -yRot, Math.clamp(-xRot, -90.0F, 90.0F)); + return FastNMS.INSTANCE.constructor$ClientboundEntityPositionSyncPacket(entityId, newPositionMoveRotation, onGround); + } + + public static Object buildCustomTridentMove(Object packet, int entityId) { + short xa = FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$xa(packet); + short ya = FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$ya(packet); + short za = FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$za(packet); + float xRot = MCUtils.unpackDegrees(FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$xRot(packet)); + float yRot = MCUtils.unpackDegrees(FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$yRot(packet)); + boolean onGround = FastNMS.INSTANCE.field$ClientboundMoveEntityPacket$onGround(packet); + return FastNMS.INSTANCE.constructor$ClientboundMoveEntityPacket$PosRot( + entityId, xa, ya, za, + MCUtils.packDegrees(-yRot), MCUtils.packDegrees(MCUtils.clamp(-xRot, -90.0F, 90.0F)), + onGround + ); + } + + public static List buildCustomTridentSetEntityDataPacket(NetWorkUser user, List packedItems, int entityId) { + List newPackedItems = new ArrayList<>(); + for (Object packedItem : packedItems) { + int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem); + if (entityDataId < 8) { + newPackedItems.add(packedItem); + } + } + List newData = user.tridentView().getOrDefault(entityId, List.of()); + if (newData.isEmpty()) { + Trident trident = getTridentById(user, entityId); + if (notCustomTrident(trident)) return newPackedItems; + newData = buildEntityDataValues(trident); + user.tridentView().put(entityId, newData); + } + newPackedItems.addAll(newData); + return newPackedItems; + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ParticleUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ParticleUtils.java index 6e847f6f9..e401be613 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ParticleUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/ParticleUtils.java @@ -12,6 +12,7 @@ public class ParticleUtils { return switch (particle) { case "REDSTONE" -> Particle.valueOf("DUST"); case "VILLAGER_HAPPY", "HAPPY_VILLAGER" -> Particle.valueOf(VersionHelper.isOrAbove1_20_5() ? "HAPPY_VILLAGER" : "VILLAGER_HAPPY"); + case "BUBBLE" -> Particle.valueOf(VersionHelper.isOrAbove1_20_5() ? "BUBBLE" : "WATER_BUBBLE"); default -> Particle.valueOf(particle); }; } 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 3ffed4d5b..035a3e303 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 @@ -3810,6 +3809,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 { @@ -3829,6 +3830,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); } @@ -3979,7 +3984,7 @@ public class Reflections { ) ); - // 1.21.3+ + // 1.21.2+ public static final Class clazz$ClientboundEntityPositionSyncPacket = ReflectionUtils.getClazz( BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundEntityPositionSyncPacket") @@ -6098,6 +6103,10 @@ public class Reflections { ) ); + public static final Constructor constructor$ClientboundMoveEntityPacket$PosRot = requireNonNull( + ReflectionUtils.getTheOnlyConstructor(clazz$ClientboundMoveEntityPacket$PosRot) + ); + public static final Class clazz$ClientboundRotateHeadPacket = requireNonNull( BukkitReflectionUtils.findReobfOrMojmapClass( "network.protocol.game.PacketPlayOutEntityHeadRotation", @@ -6395,14 +6404,18 @@ public class Reflections { ); public static final int instance$EntityType$BLOCK_DISPLAY$registryId; + public static final int instance$EntityType$ITEM_DISPLAY$registryId; public static final int instance$EntityType$TEXT_DISPLAY$registryId; public static final int instance$EntityType$FALLING_BLOCK$registryId; + public static final int instance$EntityType$TRIDENT$registryId; static { try { instance$EntityType$BLOCK_DISPLAY$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$BLOCK_DISPLAY); + instance$EntityType$ITEM_DISPLAY$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$ITEM_DISPLAY); instance$EntityType$TEXT_DISPLAY$registryId = (int) Reflections.method$Registry$getId.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, instance$EntityType$TEXT_DISPLAY); 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); } catch (Exception e) { throw new RuntimeException(e); } @@ -6618,4 +6631,5 @@ public class Reflections { BukkitReflectionUtils.assembleCBClass("block.CraftBlockStates$BlockEntityStateFactory") ) ); + } diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/CustomTrident.java b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomTrident.java new file mode 100644 index 000000000..aed870c82 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/CustomTrident.java @@ -0,0 +1,8 @@ +package net.momirealms.craftengine.core.entity; + +import net.momirealms.craftengine.core.util.Key; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +public record CustomTrident(Key customTridentItemId, Byte displayType, Vector3f translation, Quaternionf rotationLefts) { +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java index 59d8a06a4..d9968d902 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java +++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemSettings.java @@ -1,5 +1,6 @@ package net.momirealms.craftengine.core.item; +import net.momirealms.craftengine.core.entity.CustomTrident; import net.momirealms.craftengine.core.item.modifier.EquippableModifier; import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; import net.momirealms.craftengine.core.pack.misc.EquipmentGeneration; @@ -9,6 +10,8 @@ import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.VersionHelper; import org.jetbrains.annotations.Nullable; +import org.joml.Quaternionf; +import org.joml.Vector3f; import java.util.*; import java.util.stream.Collectors; @@ -22,6 +25,7 @@ public class ItemSettings { List anvilRepairItems = List.of(); boolean renameable = true; boolean canPlaceRelatedVanillaBlock = false; + CustomTrident customTrident; private ItemSettings() {} @@ -50,6 +54,7 @@ public class ItemSettings { newSettings.anvilRepairItems = settings.anvilRepairItems; newSettings.renameable = settings.renameable; newSettings.canPlaceRelatedVanillaBlock = settings.canPlaceRelatedVanillaBlock; + newSettings.customTrident = settings.customTrident; return newSettings; } @@ -65,6 +70,10 @@ public class ItemSettings { return settings; } + public CustomTrident customTrident() { + return customTrident; + } + public boolean canPlaceRelatedVanillaBlock() { return canPlaceRelatedVanillaBlock; } @@ -109,6 +118,11 @@ public class ItemSettings { return this; } + public ItemSettings customTrident(CustomTrident customTrident) { + this.customTrident = customTrident; + return this; + } + public ItemSettings canPlaceRelatedVanillaBlock(boolean canPlaceRelatedVanillaBlock) { this.canPlaceRelatedVanillaBlock = canPlaceRelatedVanillaBlock; return this; @@ -193,6 +207,14 @@ public class ItemSettings { boolean bool = (boolean) value; return settings -> settings.canPlaceRelatedVanillaBlock(bool); })); + registerFactory("custom-trident", (value -> { + Map args = MiscUtils.castToMap(value, false); + Key customTridentItemId = Key.of(args.get("custom-trident-item").toString()); + Byte displayType = Byte.valueOf(args.get("display-type").toString()); + Vector3f translation = MiscUtils.getAsVector3f(args.get("translation"), "translation"); + Quaternionf rotationLefts = MiscUtils.getAsQuaternionf(args.get("rotation-left"), "rotation-left"); + return settings -> settings.customTrident(new CustomTrident(customTridentItemId, displayType, translation, rotationLefts)); + })); } private static void registerFactory(String id, ItemSettings.Modifier.Factory factory) { 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..177e8d12c 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 @@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.plugin.network; import io.netty.channel.Channel; import net.momirealms.craftengine.core.plugin.Plugin; +import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask; import net.momirealms.craftengine.core.util.Key; import org.jetbrains.annotations.ApiStatus; @@ -46,6 +47,10 @@ public interface NetWorkUser { Map entityView(); + Map> tridentView(); + + Map tridentTaskView(); + boolean clientModEnabled(); void setClientModState(boolean enable); diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MCUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/MCUtils.java index 9669fe006..ed472a652 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/MCUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MCUtils.java @@ -184,4 +184,29 @@ public class MCUtils { } return next; } + + public static int floor(float value) { + int i = (int) value; + return value < (float)i ? i - 1 : i; + } + + public static byte packDegrees(float degrees) { + return (byte)floor(degrees * 256.0F / 360.0F); + } + + public static float unpackDegrees(byte degrees) { + return (float)(degrees * 360) / 256.0F; + } + + public static int clamp(int value, int min, int max) { + return Math.min(Math.max(value, min), max); + } + + public static float clamp(float value, float min, float max) { + return value < min ? min : Math.min(value, max); + } + + public static double clamp(double value, double min, double max) { + return value < min ? min : Math.min(value, max); + } } diff --git a/gradle.properties b/gradle.properties index ccae7e353..9acb04c0c 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.15 -nms_helper_version=0.65.15 +nms_helper_version=0.65.17 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.31.23