diff --git a/build.gradle.kts b/build.gradle.kts index 0113d58..49ae9a3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,7 +11,7 @@ plugins { } group = "me.lojosho" -version = "0.8.0${getGitCommitHash()}" +version = "0.8.1${getGitCommitHash()}" allprojects { apply(plugin = "java") @@ -130,6 +130,7 @@ dependencies { implementation(project(path = ":v1_21_R3", configuration = "reobf")) implementation(project(path = ":v1_21_R4", configuration = "reobf")) implementation(project(path = ":v1_21_R5", configuration = "reobf")) + implementation(project(path = ":v1_21_R6", configuration = "reobf")) } tasks { @@ -164,6 +165,7 @@ tasks { dependsOn(":v1_21_R3:reobfJar") dependsOn(":v1_21_R4:reobfJar") dependsOn(":v1_21_R5:reobfJar") + dependsOn(":v1_21_R6:reobfJar") mergeServiceFiles() relocate("org.bstats", "me.lojosho.shaded.bstats") diff --git a/common/src/main/java/me/lojosho/hibiscuscommons/nms/MinecraftVersion.java b/common/src/main/java/me/lojosho/hibiscuscommons/nms/MinecraftVersion.java index ce1cea8..5f83bb6 100644 --- a/common/src/main/java/me/lojosho/hibiscuscommons/nms/MinecraftVersion.java +++ b/common/src/main/java/me/lojosho/hibiscuscommons/nms/MinecraftVersion.java @@ -15,6 +15,8 @@ public enum MinecraftVersion { v1_21_6, v1_21_7, v1_21_8, + v1_21_9, + v1_21_10, ; public boolean isHigher(MinecraftVersion other) { diff --git a/common/src/main/java/me/lojosho/hibiscuscommons/nms/NMSHandlers.java b/common/src/main/java/me/lojosho/hibiscuscommons/nms/NMSHandlers.java index 007b9b5..aa3f684 100644 --- a/common/src/main/java/me/lojosho/hibiscuscommons/nms/NMSHandlers.java +++ b/common/src/main/java/me/lojosho/hibiscuscommons/nms/NMSHandlers.java @@ -22,6 +22,8 @@ public class NMSHandlers { put(MinecraftVersion.v1_21_6, new MinecraftVersionInformation("v1_21_R5", false)); put(MinecraftVersion.v1_21_7, new MinecraftVersionInformation("v1_21_R5", false)); put(MinecraftVersion.v1_21_8, new MinecraftVersionInformation("v1_21_R5", true)); + put(MinecraftVersion.v1_21_9, new MinecraftVersionInformation("v1_21_R6", false)); + put(MinecraftVersion.v1_21_10, new MinecraftVersionInformation("v1_21_R6", true)); }}; private static NMSHandler handler; diff --git a/settings.gradle.kts b/settings.gradle.kts index fa86f8d..20eef74 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -18,4 +18,5 @@ include( "v1_21_R3", "v1_21_R4", "v1_21_R5", + "v1_21_R6", ) \ No newline at end of file diff --git a/v1_21_R6/build.gradle.kts b/v1_21_R6/build.gradle.kts new file mode 100644 index 0000000..212c711 --- /dev/null +++ b/v1_21_R6/build.gradle.kts @@ -0,0 +1,30 @@ +plugins { + id("java") + id("io.papermc.paperweight.userdev") +} + +dependencies { + paperweight.paperDevBundle("1.21.10-R0.1-SNAPSHOT") + implementation(project(":common")) +} + +tasks { + + build { + dependsOn(reobfJar) + } + + compileJava { + options.encoding = Charsets.UTF_8.name() + } + java { + toolchain.languageVersion.set(JavaLanguageVersion.of(21)); + } + + javadoc { + options.encoding = Charsets.UTF_8.name() + } + processResources { + filteringCharset = Charsets.UTF_8.name() + } +} \ No newline at end of file diff --git a/v1_21_R6/src/main/java/me/lojosho/hibiscuscommons/nms/v1_21_R6/NMSCommon.java b/v1_21_R6/src/main/java/me/lojosho/hibiscuscommons/nms/v1_21_R6/NMSCommon.java new file mode 100644 index 0000000..39eefe4 --- /dev/null +++ b/v1_21_R6/src/main/java/me/lojosho/hibiscuscommons/nms/v1_21_R6/NMSCommon.java @@ -0,0 +1,16 @@ +package me.lojosho.hibiscuscommons.nms.v1_21_R6; + +import net.minecraft.network.protocol.Packet; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerConnection; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.entity.Player; + +public class NMSCommon { + + public void sendPacket(Player player, Packet packet) { + ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + ServerPlayerConnection connection = serverPlayer.connection; + connection.send(packet); + } +} diff --git a/v1_21_R6/src/main/java/me/lojosho/hibiscuscommons/nms/v1_21_R6/NMSPacketChannel.java b/v1_21_R6/src/main/java/me/lojosho/hibiscuscommons/nms/v1_21_R6/NMSPacketChannel.java new file mode 100644 index 0000000..9e1e7c8 --- /dev/null +++ b/v1_21_R6/src/main/java/me/lojosho/hibiscuscommons/nms/v1_21_R6/NMSPacketChannel.java @@ -0,0 +1,289 @@ +package me.lojosho.hibiscuscommons.nms.v1_21_R6; + +import com.mojang.datafixers.util.Pair; +import io.netty.channel.ChannelDuplexHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPromise; +import lombok.Getter; +import me.lojosho.hibiscuscommons.nms.NMSHandlers; +import me.lojosho.hibiscuscommons.packets.PacketAction; +import me.lojosho.hibiscuscommons.packets.wrapper.*; +import me.lojosho.hibiscuscommons.plugins.SubPlugins; +import me.lojosho.hibiscuscommons.util.MessagesUtil; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.*; +import net.minecraft.world.entity.ai.attributes.AttributeModifier; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.inventory.ClickType; +import net.minecraft.world.item.ItemStack; +import org.bukkit.craftbukkit.CraftEquipmentSlot; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.EquipmentSlot; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +public class NMSPacketChannel extends ChannelDuplexHandler { + + @Getter + private final Player player; + + public NMSPacketChannel(Player player) { + this.player = player; + } + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + if (!(msg instanceof Packet packet)) { + super.write(ctx, msg, promise); + return; + } + + switch (packet) { + case ClientboundContainerSetContentPacket setContentPacket -> msg = handleMenuChange(setContentPacket); + case ClientboundContainerSetSlotPacket setSlotPacket -> msg = handleSlotChange(setSlotPacket); + case ClientboundSetEquipmentPacket equipmentPacket -> msg = handlePlayerEquipment(equipmentPacket); + case ClientboundSetPassengersPacket passengerPacket -> msg = handlePassengerSet(passengerPacket); + case ClientboundUpdateAttributesPacket attributesPacket -> msg = handleScaleChange(attributesPacket); + default -> {} + } + + if (msg == null) return; + else super.write(ctx, msg, promise); + } + + private Packet handleMenuChange(@NotNull ClientboundContainerSetContentPacket packet) { + MessagesUtil.sendDebugMessages("ClientboundContainerSetContentPacket"); + MessagesUtil.sendDebugMessages("Menu Initial "); + + Integer windowId = packet.containerId(); + List slotData = packet.items(); + + List bukkitItems = new ArrayList<>(); + for (ItemStack nmsItem : slotData) { + if (nmsItem == null) { + slotData.add(null); + continue; + } + bukkitItems.add(CraftItemStack.asBukkitCopy(nmsItem)); + } + + AtomicReference action = new AtomicReference<>(PacketAction.NOTHING); + ContainerContentWrapper wrapper = new ContainerContentWrapper(windowId, bukkitItems); + SubPlugins.getSubPlugins().forEach(plugin -> { + + PacketAction pluginAction = plugin.getPacketInterface().writeContainerContent(player, wrapper); + if (pluginAction != PacketAction.NOTHING) action.set(pluginAction); + }); + + if (action.get() == PacketAction.CANCELLED) return null; + if (action.get() == PacketAction.NOTHING) return packet; + + List nmsItems = new ArrayList<>(); + for (org.bukkit.inventory.ItemStack bukkitItem : bukkitItems) { + if (bukkitItem == null) { + slotData.add(null); + continue; + } + nmsItems.add(CraftItemStack.asNMSCopy(bukkitItem)); + } + + return new ClientboundContainerSetContentPacket(wrapper.getWindowId(), packet.stateId(), nmsItems, packet.carriedItem()); + } + + private Packet handleSlotChange(@NotNull ClientboundContainerSetSlotPacket packet) { + MessagesUtil.sendDebugMessages("ClientboundContainerSetSlotPacket"); + + final int windowId = packet.getContainerId(); + final int slot = packet.getSlot(); + final ItemStack item = packet.getItem(); + + org.bukkit.inventory.ItemStack bukkitItem = CraftItemStack.asBukkitCopy(item); + + AtomicReference action = new AtomicReference<>(PacketAction.NOTHING); + SlotContentWrapper wrapper = new SlotContentWrapper(windowId, slot, bukkitItem); + + SubPlugins.getSubPlugins().forEach(plugin -> { + PacketAction pluginAction = plugin.getPacketInterface().writeSlotContent(player, wrapper); + if (pluginAction != PacketAction.NOTHING) action.set(pluginAction); + }); + + if (action.get() == PacketAction.CANCELLED) return null; + if (action.get() == PacketAction.NOTHING) return packet; + + final ItemStack nmsItem = CraftItemStack.asNMSCopy(wrapper.getItemStack()); + + return new ClientboundContainerSetSlotPacket(packet.getContainerId(), packet.getStateId(), wrapper.getSlot(), nmsItem); + } + + private Packet handlePlayerEquipment(@NotNull ClientboundSetEquipmentPacket packet) { + MessagesUtil.sendDebugMessages("ClientboundSetEquipmentPacket"); + final List> nmsArmor = packet.getSlots(); + final int entity = packet.getEntity(); + HashMap bukkitArmor = new HashMap<>(); + for (Pair piece : nmsArmor) { + EquipmentSlot slot = CraftEquipmentSlot.getSlot(piece.getFirst()); + org.bukkit.inventory.ItemStack itemStack = CraftItemStack.asBukkitCopy(piece.getSecond()); + bukkitArmor.put(slot, itemStack); + } + + AtomicReference action = new AtomicReference<>(PacketAction.NOTHING); + EntityEquipmentWrapper wrapper = new EntityEquipmentWrapper(entity, bukkitArmor); + + SubPlugins.getSubPlugins().forEach(plugin -> { + PacketAction pluginAction = plugin.getPacketInterface().writeEquipmentContent(player, wrapper); + if (pluginAction != PacketAction.NOTHING) action.set(pluginAction); + }); + + if (action.get() == PacketAction.CANCELLED) return null; + if (action.get() == PacketAction.NOTHING) return packet; + + List> newArmor = new ArrayList<>(); + for (Map.Entry entry : wrapper.getArmor().entrySet()) { + net.minecraft.world.entity.EquipmentSlot slot = CraftEquipmentSlot.getNMS(entry.getKey()); + ItemStack itemStack = CraftItemStack.asNMSCopy(entry.getValue()); + newArmor.add(new Pair<>(slot, itemStack)); + } + + return new ClientboundSetEquipmentPacket(packet.getEntity(), newArmor); + } + + private Packet handlePassengerSet(@NotNull ClientboundSetPassengersPacket packet) { + MessagesUtil.sendDebugMessages("ClientboundSetPassengersPacket"); + int ownerId = packet.getVehicle(); + List passengers = Arrays.stream(packet.getPassengers()).boxed().collect(Collectors.toList()); + MessagesUtil.sendDebugMessages("Mount Packet Sent - Read - EntityID: " + ownerId); + + AtomicReference action = new AtomicReference<>(PacketAction.NOTHING); + PassengerWrapper wrapper = new PassengerWrapper(ownerId, passengers); + SubPlugins.getSubPlugins().forEach(plugin -> { + PacketAction pluginAction = plugin.getPacketInterface().writePassengerContent(player, wrapper); + if (pluginAction != PacketAction.NOTHING) { + action.set(pluginAction); + } + }); + + if (action.get() == PacketAction.CANCELLED) return null; + if (action.get() == PacketAction.NOTHING) return packet; + return (Packet) NMSHandlers.getHandler().getPacketHandler().createMountPacket(ownerId, passengers.stream().mapToInt(Integer::intValue).toArray()); + } + + private Packet handleScaleChange(@NotNull ClientboundUpdateAttributesPacket packet) { + final List nmsAttributes = packet.getValues(); + final ClientboundUpdateAttributesPacket.AttributeSnapshot nmsScaleAttribute = nmsAttributes.stream() + .filter(attribute -> attribute.attribute().equals(Attributes.SCALE)) + .findFirst() + .orElse(null); + + if (nmsScaleAttribute == null) { + return packet; + } + + AtomicReference action = new AtomicReference<>(PacketAction.NOTHING); + + final double base = nmsScaleAttribute.base(); + double total = base; + + for (AttributeModifier modifier : nmsScaleAttribute.modifiers()) { + switch (modifier.operation()) { + case ADD_VALUE -> total += modifier.amount(); + case ADD_MULTIPLIED_BASE -> total += modifier.amount() * nmsScaleAttribute.base(); + case ADD_MULTIPLIED_TOTAL -> total += modifier.amount() * total; + } + } + PlayerScaleWrapper wrapper = new PlayerScaleWrapper(packet.getEntityId(), base, total); + + SubPlugins.getSubPlugins().forEach(plugin -> { + PacketAction pluginAction = plugin.getPacketInterface().readPlayerScale(player, wrapper); + if (pluginAction != PacketAction.NOTHING) action.set(pluginAction); + }); + + if (action.get() == PacketAction.CANCELLED) return null; + if (action.get() == PacketAction.NOTHING) return packet; + return packet; + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + if (!(msg instanceof Packet packet)) { + super.channelRead(ctx, msg); + return; + } + + switch (packet) { + case ServerboundContainerClickPacket clickPacket -> msg = handleInventoryClick(clickPacket); + case ServerboundPlayerActionPacket playerActionPacket -> msg = handlePlayerAction(playerActionPacket); + case ServerboundSwingPacket swingPacket -> msg = handlePlayerArm(swingPacket); + case ServerboundInteractPacket interactPacket -> msg = handleInteract(interactPacket); + default -> {} + } + + if (msg == null) return; + else super.channelRead(ctx, msg); + } + + private Packet handleInventoryClick(@NotNull ServerboundContainerClickPacket packet) { + MessagesUtil.sendDebugMessages("ServerboundContainerClickPacket"); + ClickType clickType = packet.clickType(); + int slotClicked = packet.slotNum(); + + AtomicReference action = new AtomicReference<>(PacketAction.NOTHING); + SubPlugins.getSubPlugins().forEach(plugin -> { + + PacketAction pluginAction = plugin.getPacketInterface().readInventoryClick(player, new InventoryClickWrapper(clickType.id(), slotClicked)); + if (pluginAction != PacketAction.NOTHING) action.set(pluginAction); + + }); + if (action.get() == PacketAction.CANCELLED) return null; + return packet; + } + + private Packet handlePlayerAction(ServerboundPlayerActionPacket packet) { + MessagesUtil.sendDebugMessages("ServerboundPlayerActionPacket"); + ServerboundPlayerActionPacket.Action playerAction = packet.getAction(); + + AtomicReference action = new AtomicReference<>(PacketAction.NOTHING); + SubPlugins.getSubPlugins().forEach(plugin -> { + + PacketAction pluginAction = plugin.getPacketInterface().readPlayerAction(player, new PlayerActionWrapper(playerAction.name())); + if (pluginAction != PacketAction.NOTHING) action.set(pluginAction); + + }); + if (action.get() == PacketAction.CANCELLED) return null; + return packet; + } + + private Packet handlePlayerArm(@NotNull ServerboundSwingPacket packet) { + MessagesUtil.sendDebugMessages("ServerboundSwingPacket"); + PlayerSwingWrapper wrapper = new PlayerSwingWrapper(packet.getHand().name()); + + AtomicReference action = new AtomicReference<>(PacketAction.NOTHING); + SubPlugins.getSubPlugins().forEach(plugin -> { + + PacketAction pluginAction = plugin.getPacketInterface().readPlayerArm(player, wrapper); + if (pluginAction != PacketAction.NOTHING) action.set(pluginAction); + + }); + if (action.get() == PacketAction.CANCELLED) return null; + return packet; + } + + private Packet handleInteract(@NotNull ServerboundInteractPacket packet) { + MessagesUtil.sendDebugMessages("ServerboundInteractPacket"); + + PlayerInteractWrapper wrapper = new PlayerInteractWrapper(packet.getEntityId()); + + AtomicReference action = new AtomicReference<>(PacketAction.NOTHING); + SubPlugins.getSubPlugins().forEach(plugin -> { + + PacketAction pluginAction = plugin.getPacketInterface().readEntityHandle(player, wrapper); + if (pluginAction != PacketAction.NOTHING) action.set(pluginAction); + + }); + if (action.get() == PacketAction.CANCELLED) return null; + return packet; + } +} \ No newline at end of file diff --git a/v1_21_R6/src/main/java/me/lojosho/hibiscuscommons/nms/v1_21_R6/NMSPackets.java b/v1_21_R6/src/main/java/me/lojosho/hibiscuscommons/nms/v1_21_R6/NMSPackets.java new file mode 100644 index 0000000..571c53d --- /dev/null +++ b/v1_21_R6/src/main/java/me/lojosho/hibiscuscommons/nms/v1_21_R6/NMSPackets.java @@ -0,0 +1,564 @@ +package me.lojosho.hibiscuscommons.nms.v1_21_R6; + +import com.google.common.collect.ImmutableList; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.JsonOps; +import io.papermc.paper.adventure.PaperAdventure; +import it.unimi.dsi.fastutil.ints.IntList; +import me.lojosho.hibiscuscommons.HibiscusCommonsPlugin; +import me.lojosho.hibiscuscommons.util.AdventureUtils; +import me.lojosho.hibiscuscommons.util.MessagesUtil; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.minecraft.advancements.Advancement; +import net.minecraft.advancements.AdvancementHolder; +import net.minecraft.commands.arguments.EntityAnchorArgument; +import net.minecraft.network.chat.RemoteChatSession; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.*; +import net.minecraft.network.syncher.EntityDataSerializers; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.PositionMoveRotation; +import net.minecraft.world.entity.ai.attributes.AttributeInstance; +import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.entity.decoration.ArmorStand; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.level.GameType; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.scores.PlayerTeam; +import net.minecraft.world.scores.Team; +import org.bukkit.*; +import org.bukkit.craftbukkit.CraftEquipmentSlot; +import org.bukkit.craftbukkit.entity.CraftEntityType; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.craftbukkit.scoreboard.CraftScoreboard; +import org.bukkit.entity.*; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.components.CustomModelDataComponent; +import org.jetbrains.annotations.NotNull; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +import java.util.*; +import java.util.stream.Collectors; + +public class NMSPackets extends NMSCommon implements me.lojosho.hibiscuscommons.nms.NMSPackets { + + private static ServerLevel level = MinecraftServer.getServer().overworld(); + private static final Map CLOUD_EFFECT_INVISIBLE_DATA_VALUES = Map.of(0, (byte) 0x20, 8, 0f); // For cloud effects + private static final Map GENERIC_INVISIBLE_DATA_VALUES = Map.of(0, (byte) 0x20); // For most entities if you just need genericaly invisible + private static Entity fakeNmsEntity = new ArmorStand(net.minecraft.world.entity.EntityType.ARMOR_STAND, level); + + @Override @SuppressWarnings("unchecked") + public void sendSharedEntityData(int entityId, Map dataValues, List sendTo) { + ClientboundSetEntityDataPacket packet = getSharedEntityPacket(entityId, dataValues); + for (Player player : sendTo) sendPacket(player, packet); + } + + private ClientboundSetEntityDataPacket getSharedEntityPacket(int entityId, Map dataValues) { + List> nmsDataValues = dataValues.entrySet().stream().map(entry -> { + int index = entry.getKey(); + Number value = entry.getValue(); + return switch (value) { + case Byte byteVal -> new SynchedEntityData.DataValue<>(index, EntityDataSerializers.BYTE, byteVal); + case Float floatVal -> new SynchedEntityData.DataValue<>(index, EntityDataSerializers.FLOAT, floatVal); + case Integer intVal -> new SynchedEntityData.DataValue<>(index, EntityDataSerializers.INT, intVal); + default -> + throw new IllegalArgumentException("Unsupported data value type: " + value.getClass().getSimpleName()); + }; + }).collect(Collectors.toList()); + + return new ClientboundSetEntityDataPacket(entityId, nmsDataValues); + } + + @Override + public void sendFakePlayerInfoPacket( + final Player skinnedPlayer, + final int entityId, + final UUID uuid, + final String npcName, + final List sendTo + ) { + ServerPlayer player = ((CraftPlayer) skinnedPlayer).getHandle(); + String name = npcName; + if (name.length() > 15) name = name.substring(0, 15); + Property property = ((CraftPlayer) skinnedPlayer).getProfile().properties().get("textures").stream().findAny().orElse(null); + + GameProfile profile = new GameProfile(uuid, name); + if (property != null) profile.properties().put("textures", property); + + Component component = AdventureUtils.MINI_MESSAGE.deserialize(name); + net.minecraft.network.chat.Component nmsComponent = HibiscusCommonsPlugin.isOnPaper() ? PaperAdventure.asVanilla(component) : net.minecraft.network.chat.Component.literal(name); + + RemoteChatSession.Data chatData = null; + RemoteChatSession session = player.getChatSession(); + if (session != null) chatData = player.getChatSession().asData(); + + ClientboundPlayerInfoUpdatePacket.Entry entry = new ClientboundPlayerInfoUpdatePacket.Entry(uuid, profile, false, 0, GameType.CREATIVE, nmsComponent, true, player.listOrder, chatData); + EnumSet actions = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER); + ClientboundPlayerInfoUpdatePacket packet = new ClientboundPlayerInfoUpdatePacket(actions, entry); + for (Player p : sendTo) sendPacket(p, packet); + } + + @Override + public void sendPlayerInfoRemovePacket(final UUID uuid, final List sendTo) { + ClientboundPlayerInfoRemovePacket packet = new ClientboundPlayerInfoRemovePacket(List.of(uuid)); + for (Player player : sendTo) sendPacket(player, packet); + } + + @Override + public void sendMovePacket( + final int entityId, + final @NotNull Location from, + final @NotNull Location to, + final boolean onGround, + @NotNull List sendTo + ) { + byte dx = (byte) (to.getX() - from.getX()); + byte dy = (byte) (to.getY() - from.getY()); + byte dz = (byte) (to.getZ() - from.getZ()); + + ClientboundMoveEntityPacket.Pos packet = new ClientboundMoveEntityPacket.Pos(entityId, dx, dy, dz, onGround); + for (Player p : sendTo) sendPacket(p, packet); + } + + @Override + public void sendGamemodeChange(Player player, GameMode gameMode) { + ClientboundGameEventPacket.Type type = ClientboundGameEventPacket.CHANGE_GAME_MODE; + float param = gameMode.getValue(); + + ClientboundGameEventPacket packet = new ClientboundGameEventPacket(type, param); + sendPacket(player, packet); + } + + @Override + public void sendLookAtPacket(int entityId, Location location, List sendTo) { + fakeNmsEntity.setId(entityId); + fakeNmsEntity.getBukkitEntity().teleport(location); + ClientboundPlayerLookAtPacket packet = new ClientboundPlayerLookAtPacket(EntityAnchorArgument.Anchor.EYES, fakeNmsEntity, EntityAnchorArgument.Anchor.EYES); + for (Player p : sendTo) sendPacket(p, packet); + } + + @Override + public void sendRotateHeadPacket(int entityId, Location location, List sendTo) { + fakeNmsEntity.setId(entityId); + byte headRot = (byte) (location.getYaw() * 256.0F / 360.0F); + + ClientboundRotateHeadPacket packet = new ClientboundRotateHeadPacket(fakeNmsEntity, headRot); + for (Player p : sendTo) sendPacket(p, packet); + } + + @Override + public void sendRotationPacket(int entityId, float originalYaw, float pitch, boolean onGround, List sendTo) { + float ROTATION_FACTOR = 256.0F / 360.0F; + byte yaw = (byte) (originalYaw * ROTATION_FACTOR); + pitch = (byte) (pitch * ROTATION_FACTOR); + MessagesUtil.sendDebugMessages("sendRotationPacket. Original: " + originalYaw + " modified: " + yaw); + ClientboundMoveEntityPacket.Rot packet = new ClientboundMoveEntityPacket.Rot(entityId, (byte) yaw, (byte) pitch, onGround); + for (Player p : sendTo) sendPacket(p, packet); + } + + @Override + public void sendRotationPacket(int entityId, Location location, boolean onGround, List sendTo) { + float ROTATION_FACTOR = 256.0F / 360.0F; + byte yaw = (byte) (location.getYaw() * ROTATION_FACTOR); + byte pitch = (byte) (location.getPitch() * ROTATION_FACTOR); + ClientboundMoveEntityPacket.Rot packet = new ClientboundMoveEntityPacket.Rot(entityId, yaw, pitch, onGround); + for (Player p : sendTo) sendPacket(p, packet); + } + + @Override + public void sendEquipmentSlotUpdate( + int entityId, + org.bukkit.inventory.EquipmentSlot slot, + ItemStack item, + List sendTo + ) { + + EquipmentSlot nmsSlot = null; + net.minecraft.world.item.ItemStack nmsItem = null; + + // Converting EquipmentSlot and ItemStack to NMS ones. + nmsSlot = CraftEquipmentSlot.getNMS(slot); + nmsItem = CraftItemStack.asNMSCopy(item); + + if (nmsSlot == null) return; + + Pair pair = new Pair<>(nmsSlot, nmsItem); + + List> pairs = Collections.singletonList(pair); + + ClientboundSetEquipmentPacket packet = new ClientboundSetEquipmentPacket(entityId, pairs); + for (Player p : sendTo) sendPacket(p, packet); + } + + @Override + public void sendEquipmentSlotUpdate( + int entityId, + HashMap equipment, + List sendTo + ) { + + List> pairs = new ArrayList<>(); + + for (org.bukkit.inventory.EquipmentSlot slot : equipment.keySet()) { + EquipmentSlot nmsSlot = CraftEquipmentSlot.getNMS(slot); + net.minecraft.world.item.ItemStack nmsItem = CraftItemStack.asNMSCopy(equipment.get(slot)); + + Pair pair = new Pair<>(nmsSlot, nmsItem); + pairs.add(pair); + } + + ClientboundSetEquipmentPacket packet = new ClientboundSetEquipmentPacket(entityId, pairs); + for (Player p : sendTo) sendPacket(p, packet); + } + + + @Override + public void sendSlotUpdate( + Player player, + int slot + ) { + int index = 0; + + ServerPlayer player1 = ((CraftPlayer) player).getHandle(); + + if (index < Inventory.getSelectionSize()) { + index += 36; + } else if (index > 39) { + index += 5; // Off hand + } else if (index > 35) { + index = 8 - (index - 36); + } + ItemStack item = player.getInventory().getItem(slot); + + Packet packet = new ClientboundContainerSetSlotPacket(player1.inventoryMenu.containerId, player1.inventoryMenu.incrementStateId(), index, CraftItemStack.asNMSCopy(item)); + sendPacket(player, packet); + } + + @Override + public void sendScoreboardHideNamePacket(Player player, String name) { + //Creating the team + PlayerTeam team = new PlayerTeam(((CraftScoreboard) Bukkit.getScoreboardManager().getMainScoreboard()).getHandle(), name); + + //Setting name visibility + team.setNameTagVisibility(Team.Visibility.NEVER); + + //Remove the Team (i assume so if it exists) + ClientboundSetPlayerTeamPacket removeTeamPacket = ClientboundSetPlayerTeamPacket.createRemovePacket(team); + //Creating the Team + ClientboundSetPlayerTeamPacket createTeamPacket = ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, true); + //Adding players to the team (You have to use the NPC's name, and add it to a list) + ClientboundSetPlayerTeamPacket createPlayerTeamPacket = ClientboundSetPlayerTeamPacket.createMultiplePlayerPacket(team, new ArrayList() {{ + add(name); + add(player.getName()); + }}, ClientboundSetPlayerTeamPacket.Action.ADD); + + ClientboundBundlePacket bundlePacket = new ClientboundBundlePacket(List.of(removeTeamPacket, createTeamPacket, createPlayerTeamPacket)); + sendPacket(player, bundlePacket); + } + + + @Override + public void sendMountPacket(int mountId, int[] passengerIds, List sendTo) { + List passengers = Arrays.stream(passengerIds).mapToObj(id -> { + Entity passenger = new ArmorStand(net.minecraft.world.entity.EntityType.ARMOR_STAND, level); + passenger.setId(id); + return passenger; + }).toList(); + fakeNmsEntity.setId(mountId); + fakeNmsEntity.passengers = ImmutableList.copyOf(passengers); + ClientboundSetPassengersPacket packet = new ClientboundSetPassengersPacket(fakeNmsEntity); + fakeNmsEntity.passengers = ImmutableList.of(); + for (Player p : sendTo) sendPacket(p, packet); + } + + @Override + public void sendLeashPacket(int leashEntity, int entityId, List sendTo) { + // Fake entities just to avoid reflection + ServerLevel level = MinecraftServer.getServer().overworld(); + Entity entity1 = new ArmorStand(net.minecraft.world.entity.EntityType.ARMOR_STAND, level); + Entity entity2 = new ArmorStand(net.minecraft.world.entity.EntityType.ARMOR_STAND, level); + entity1.setId(leashEntity); + entity2.setId(entityId); + + ClientboundSetEntityLinkPacket packet = new ClientboundSetEntityLinkPacket(entity1, entity2); + for (Player p : sendTo) sendPacket(p, packet); + } + + @Override + public void sendTeleportPacket( + int entityId, + double x, + double y, + double z, + float yaw, + float pitch, + boolean onGround, + List sendTo + ) { + try { + ClientboundTeleportEntityPacket packet = ClientboundTeleportEntityPacket.teleport(entityId, new PositionMoveRotation(new Vec3(x, y, z), Vec3.ZERO, yaw, pitch), Set.of(), onGround); + for (Player p : sendTo) sendPacket(p, packet); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void sendCameraPacket(int entityId, List sendTo) { + fakeNmsEntity.setId(entityId); + + ClientboundSetCameraPacket packet = new ClientboundSetCameraPacket(fakeNmsEntity); + for (Player p : sendTo) sendPacket(p, packet); + } + + + @Override + public void sendSpawnEntityPacket(int entityId, UUID uuid, EntityType entityType, Location location, List sendTo) { + net.minecraft.world.entity.EntityType nmsEntityType = CraftEntityType.bukkitToMinecraft(entityType); + double x = location.getX(); + double y = location.getY(); + double z = location.getZ(); + float yaw = location.getYaw(); + float pitch = location.getPitch(); + Vec3 velocity = Vec3.ZERO; + float headYaw = 0f; + + ClientboundAddEntityPacket packet = new ClientboundAddEntityPacket(entityId, uuid, x, y, z, yaw, pitch, nmsEntityType, 0, velocity, headYaw); + for (Player p : sendTo) sendPacket(p, packet); + } + + @Override + public void sendEntityDestroyPacket(IntList entityIds, List sendTo) { + ClientboundRemoveEntitiesPacket packet = new ClientboundRemoveEntitiesPacket(entityIds); + for (Player p : sendTo) sendPacket(p, packet); + } + + @Override + public void sendEntityScalePacket(int entityId, double scale, List sendTo) { + AttributeInstance attribute = new AttributeInstance( + Attributes.SCALE, + (ignored) -> {} + ); + attribute.setBaseValue(scale); + + ClientboundUpdateAttributesPacket packet = new ClientboundUpdateAttributesPacket(entityId, List.of(attribute)); + for (Player p : sendTo) sendPacket(p, packet); + } + + @Override + public void sendItemDisplayMetadata(int entityId, + Vector3f translation, + Vector3f scale, + Quaternionf rotationLeft, + Quaternionf rotationRight, + Display.Billboard billboard, + int blockLight, int skyLight, float viewRange, float width, float height, + ItemDisplay.ItemDisplayTransform transform, ItemStack itemStack, + List sendTo) { + + List> dataValues = new ArrayList<>(); + dataValues.add(new SynchedEntityData.DataValue<>(10, EntityDataSerializers.INT, POSITION_INTERPOLATION_DURATION)); + dataValues.add(new SynchedEntityData.DataValue<>(11, EntityDataSerializers.VECTOR3, translation)); + dataValues.add(new SynchedEntityData.DataValue<>(12, EntityDataSerializers.VECTOR3, scale)); + dataValues.add(new SynchedEntityData.DataValue<>(13, EntityDataSerializers.QUATERNION, rotationLeft)); + dataValues.add(new SynchedEntityData.DataValue<>(14, EntityDataSerializers.QUATERNION, rotationRight)); + dataValues.add(new SynchedEntityData.DataValue<>(15, EntityDataSerializers.BYTE, (byte) billboard.ordinal())); + dataValues.add(new SynchedEntityData.DataValue<>(16, EntityDataSerializers.INT, (blockLight << 4 | skyLight << 20))); + dataValues.add(new SynchedEntityData.DataValue<>(17, EntityDataSerializers.FLOAT, viewRange)); + dataValues.add(new SynchedEntityData.DataValue<>(20, EntityDataSerializers.FLOAT, width)); + dataValues.add(new SynchedEntityData.DataValue<>(21, EntityDataSerializers.FLOAT, height)); + dataValues.add(new SynchedEntityData.DataValue<>(23, EntityDataSerializers.ITEM_STACK, CraftItemStack.asNMSCopy(itemStack))); + dataValues.add(new SynchedEntityData.DataValue<>(24, EntityDataSerializers.BYTE, (byte) transform.ordinal())); + + ClientboundSetEntityDataPacket packet = new ClientboundSetEntityDataPacket(entityId, dataValues); + for (Player p : sendTo) sendPacket(p, packet); + } + + public void sendToastPacket(Player player, ItemStack icon, Component title, Component description) { + final var key = ResourceLocation.fromNamespaceAndPath("hibiscuscommons", UUID.randomUUID().toString()); + + JsonObject json = new JsonObject(); + + // Creating the "criteria" object + JsonObject impossibleCriteria = new JsonObject(); + JsonObject impossible = new JsonObject(); + impossible.addProperty("trigger", "minecraft:impossible"); + impossibleCriteria.add("impossible", impossible); + json.add("criteria", impossibleCriteria); + + // Creating the "display" object + JsonObject display = new JsonObject(); + JsonObject iconObj = new JsonObject(); + iconObj.addProperty("id", icon.getType().getKey().toString()); + + if (icon.hasItemMeta()) { + ItemMeta meta = icon.getItemMeta(); + JsonObject components = new JsonObject(); + + if (!meta.getEnchants().isEmpty()) { + components.addProperty("minecraft:enchantment_glint_override", true); + } + + if (meta.hasCustomModelData()) { + CustomModelDataComponent customModelDataComponent = meta.getCustomModelDataComponent(); + JsonObject customModelDataComponentJson = new JsonObject(); + + List floats = customModelDataComponent.getFloats(); + if (!floats.isEmpty()) { + JsonArray floatsArray = new JsonArray(); + floats.forEach(floatsArray::add); + customModelDataComponentJson.add("floats", floatsArray); + } + + List flags = customModelDataComponent.getFlags(); + if (!flags.isEmpty()) { + JsonArray flagsArray = new JsonArray(); + flags.forEach(flagsArray::add); + customModelDataComponentJson.add("flags", flagsArray); + } + + List strings = customModelDataComponent.getStrings(); + if (!strings.isEmpty()) { + JsonArray stringsArray = new JsonArray(); + strings.forEach(stringsArray::add); + customModelDataComponentJson.add("strings", stringsArray); + } + + List colors = customModelDataComponent.getColors(); + if (!colors.isEmpty()) { + JsonArray colorsArray = new JsonArray(); + colors.forEach(color -> colorsArray.add(color.asRGB())); + customModelDataComponentJson.add("colors", colorsArray); + } + + components.add("minecraft:custom_model_data", customModelDataComponentJson); + } + + NamespacedKey itemModel = meta.getItemModel(); + if (itemModel != null) { + components.addProperty("minecraft:item_model", itemModel.toString()); + } + + iconObj.add("components", components); + } + + display.add("icon", iconObj); + display.add("title", GsonComponentSerializer.gson().serializeToTree(title)); + display.add("description", GsonComponentSerializer.gson().serializeToTree(description)); + display.addProperty("description", "Toast Description"); + display.addProperty("frame", "task"); + display.addProperty("announce_to_chat", false); + display.addProperty("show_toast", true); + display.addProperty("hidden", true); + + json.add("display", display); + + final var advancement = Advancement.CODEC.parse(MinecraftServer.getServer().registryAccess().createSerializationContext(JsonOps.INSTANCE), json); + final var advancementHolder = new AdvancementHolder(key, advancement.result().orElseThrow()); + + final var nmsPlayer = ((CraftPlayer) player).getHandle(); + final var progress = nmsPlayer.getAdvancements().getOrStartProgress(advancementHolder); + MinecraftServer.getServer().getAdvancements().tree().addAll(Set.of(advancementHolder)); + progress.getRemainingCriteria().forEach(criteria -> nmsPlayer.getAdvancements().award(advancementHolder, criteria)); + + Bukkit.getScheduler().runTaskLater(HibiscusCommonsPlugin.getInstance(), () -> { + progress.getRemainingCriteria().forEach(criteria -> nmsPlayer.getAdvancements().revoke(advancementHolder, criteria)); + MinecraftServer.getServer().getAdvancements().tree().remove(Set.of(key)); + + // Remove the advancement from the player's client to prevent it from being displayed again + // Was not working without this? + ClientboundUpdateAdvancementsPacket removePacket = new ClientboundUpdateAdvancementsPacket( + false, + Collections.emptyList(), + Set.of(key), + Map.of(), + false + ); + + sendPacket(player, removePacket); + }, 2L); + } + + @Override + public Object createMountPacket(int entityId, int[] passengerIds) { + fakeNmsEntity.setId(entityId); + List passengers = Arrays.stream(passengerIds).mapToObj(id -> { + Entity passenger = new ArmorStand(net.minecraft.world.entity.EntityType.ARMOR_STAND, level); + passenger.setId(id); + return passenger; + }).toList(); + fakeNmsEntity.passengers = ImmutableList.copyOf(passengers); + ClientboundSetPassengersPacket packet = new ClientboundSetPassengersPacket(fakeNmsEntity); + fakeNmsEntity.passengers = ImmutableList.of(); + return packet; + } + + @Override + public void sendInvisibleParticleCloud(int entityId, Location location, UUID uuid, List sendTo) { + net.minecraft.world.entity.EntityType nmsEntityType = net.minecraft.world.entity.EntityType.AREA_EFFECT_CLOUD; + double x = location.getX(); + double y = location.getY(); + double z = location.getZ(); + float yaw = location.getYaw(); + float pitch = location.getPitch(); + Vec3 velocity = Vec3.ZERO; + float headYaw = 0f; + + ClientboundAddEntityPacket spawnPacket = new ClientboundAddEntityPacket(entityId, uuid, x, y, z, yaw, pitch, nmsEntityType, 0, velocity, headYaw); + ClientboundSetEntityDataPacket dataPacket = getSharedEntityPacket(entityId, CLOUD_EFFECT_INVISIBLE_DATA_VALUES); + + ClientboundBundlePacket bundlePacket = new ClientboundBundlePacket(List.of(spawnPacket, dataPacket)); + for (Player p : sendTo) sendPacket(p, bundlePacket); + } + + public void sendInvisibleArmorstand(int entityId, Location location, UUID uuid, byte mask, List sendTo) { + net.minecraft.world.entity.EntityType nmsEntityType = net.minecraft.world.entity.EntityType.ARMOR_STAND; + double x = location.getX(); + double y = location.getY(); + double z = location.getZ(); + float yaw = location.getYaw(); + float pitch = location.getPitch(); + Vec3 velocity = Vec3.ZERO; + float headYaw = 0f; + + final ClientboundAddEntityPacket spawnPacket = new ClientboundAddEntityPacket(entityId, uuid, x, y, z, yaw, pitch, nmsEntityType, 0, velocity, headYaw); + + final Map dataValues = Map.of(0, mask, 15, (byte) 0x10); + final ClientboundSetEntityDataPacket dataPacket = getSharedEntityPacket(entityId, dataValues); + + ClientboundBundlePacket bundlePacket = new ClientboundBundlePacket(List.of(spawnPacket, dataPacket)); + for (Player p : sendTo) sendPacket(p, bundlePacket); + } + + @Override + public void sendInvisibleEntity(int entityId, EntityType type, Location location, UUID uuid, List sendTo) { + net.minecraft.world.entity.EntityType nmsEntityType = CraftEntityType.bukkitToMinecraft(type); + double x = location.getX(); + double y = location.getY(); + double z = location.getZ(); + float yaw = location.getYaw(); + float pitch = location.getPitch(); + Vec3 velocity = Vec3.ZERO; + float headYaw = 0f; + + final ClientboundAddEntityPacket spawnPacket = new ClientboundAddEntityPacket(entityId, uuid, x, y, z, yaw, pitch, nmsEntityType, 0, velocity, headYaw); + final ClientboundSetEntityDataPacket dataPacket = getSharedEntityPacket(entityId, GENERIC_INVISIBLE_DATA_VALUES); + + ClientboundBundlePacket bundlePacket = new ClientboundBundlePacket(List.of(spawnPacket, dataPacket)); + for (Player p : sendTo) sendPacket(p, bundlePacket); + } +} diff --git a/v1_21_R6/src/main/java/me/lojosho/hibiscuscommons/nms/v1_21_R6/NMSUtils.java b/v1_21_R6/src/main/java/me/lojosho/hibiscuscommons/nms/v1_21_R6/NMSUtils.java new file mode 100644 index 0000000..3739f86 --- /dev/null +++ b/v1_21_R6/src/main/java/me/lojosho/hibiscuscommons/nms/v1_21_R6/NMSUtils.java @@ -0,0 +1,86 @@ +package me.lojosho.hibiscuscommons.nms.v1_21_R6; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelPipeline; +import net.minecraft.core.component.DataComponents; +import net.minecraft.network.Connection; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.alchemy.PotionContents; +import net.minecraft.world.item.component.DyedItemColor; +import org.bukkit.Bukkit; +import org.bukkit.Color; +import org.bukkit.Material; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.PotionMeta; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Optional; + +public class NMSUtils extends NMSCommon implements me.lojosho.hibiscuscommons.nms.NMSUtils { + + @Override + public int getNextEntityId() { + return net.minecraft.world.entity.Entity.nextEntityId(); + } + + @Override + public org.bukkit.entity.Entity getEntity(int entityId) { + net.minecraft.world.entity.Entity entity = getNMSEntity(entityId); + if (entity == null) return null; + return entity.getBukkitEntity(); + } + + @Override + public @Nullable Color getColor(ItemStack itemStack) { + if (itemStack == null) return null; + net.minecraft.world.item.ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + if (nmsItem == null) return null; + + DyedItemColor color = nmsItem.get(DataComponents.DYED_COLOR); + if (color == null) return null; + return Color.fromRGB(color.rgb()); + } + + @Override + public ItemStack setColor(@NotNull ItemStack itemStack, @NotNull Color color) { + net.minecraft.world.item.ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack); + // Potions needs a bit extra to get the color working properly + if (itemStack.getType() == Material.POTION) { + nmsStack.set(DataComponents.POTION_CONTENTS, new PotionContents( + Optional.empty(), + Optional.of(color.asRGB()), + List.of(), + Optional.empty()) + ); + } + nmsStack.set(DataComponents.DYED_COLOR, new DyedItemColor(color.asRGB())); + return CraftItemStack.asBukkitCopy(nmsStack); + } + + private net.minecraft.world.entity.Entity getNMSEntity(int entityId) { + for (ServerLevel world : ((CraftServer) Bukkit.getServer()).getHandle().getServer().getAllLevels()) { + net.minecraft.world.entity.Entity entity = world.getEntity(entityId); + if (entity == null) continue; + return entity; + } + return null; + } + + @Override + public void handleChannelOpen(@NotNull Player player) { + Channel channel = ((CraftPlayer) player).getHandle().connection.connection.channel; + ChannelPipeline pipeline = channel.pipeline(); + + NMSPacketChannel channelHandler = new NMSPacketChannel(player); + for (String key : pipeline.toMap().keySet()) { + if (!(pipeline.get(key) instanceof Connection)) continue; + pipeline.addBefore(key, "hibiscus_channel_handler", channelHandler); + } + } +}