From 8374209e590ce25a10888a861d046ecd7c6cc83c Mon Sep 17 00:00:00 2001 From: Matej Pacan Date: Fri, 14 Apr 2023 09:57:26 +0200 Subject: [PATCH] Add support for replacing chat info messages in 1.19+ --- .../mineacademy/fo/model/PacketListener.java | 79 +++++++++++++++++-- 1 file changed, 73 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/mineacademy/fo/model/PacketListener.java b/src/main/java/org/mineacademy/fo/model/PacketListener.java index f9dc0cb13..571a5a653 100644 --- a/src/main/java/org/mineacademy/fo/model/PacketListener.java +++ b/src/main/java/org/mineacademy/fo/model/PacketListener.java @@ -1,5 +1,7 @@ package org.mineacademy.fo.model; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -11,6 +13,7 @@ import org.mineacademy.fo.Common; import org.mineacademy.fo.MinecraftVersion; import org.mineacademy.fo.MinecraftVersion.V; +import org.mineacademy.fo.ReflectionUtil; import org.mineacademy.fo.collection.SerializedMap; import org.mineacademy.fo.exception.EventHandledException; import org.mineacademy.fo.exception.FoException; @@ -26,11 +29,13 @@ import com.comphenix.protocol.wrappers.EnumWrappers.ChatType; import com.comphenix.protocol.wrappers.WrappedChatComponent; import com.comphenix.protocol.wrappers.WrappedGameProfile; +import com.comphenix.protocol.wrappers.WrappedServerPing; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; /** * Represents packet handling using ProtocolLib @@ -38,6 +43,12 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) public abstract class PacketListener { + /** + * Stores 1.19 system chat packet constructor and Adventure stuff for maximum performance + */ + private static Constructor packetConst; + private static Class textComponentClass; + /** * Called automatically when you use \@AutoRegister, inject * your packet listeners here. @@ -156,6 +167,24 @@ protected List compileHoverText(final String... hoverTexts) return profiles; } + // ------------------------------------------------------------------------------------------------------------ + // Stati + // ------------------------------------------------------------------------------------------------------------ + + /* + * Sends a system chat packet to the player + */ + private static void sendSystemChatPacket(Player player, String message) { + if (packetConst == null) { + Class packetClass = ReflectionUtil.lookupClass("net.minecraft.network.protocol.game.ClientboundSystemChatPacket"); + + packetConst = ReflectionUtil.getConstructor(packetClass, BaseComponent[].class, boolean.class); + } + + Object newPacket = ReflectionUtil.instantiate(packetConst, TextComponent.fromLegacyText(message), false /* not on action bar */); + Remain.sendPacket(player, newPacket); + } + // ------------------------------------------------------------------------------------------------------------ // Classes // ------------------------------------------------------------------------------------------------------------ @@ -280,12 +309,25 @@ private String compileChatMessage(PacketEvent event) { // Reset this.jsonMessage = null; - // No components for this MC version + // Components if (MinecraftVersion.atLeast(V.v1_7)) { - if (this.systemChat) + + // System chat + if (this.systemChat) { this.jsonMessage = event.getPacket().getStrings().read(0); - else { + final Object adventureContent = ReflectionUtil.getFieldContent(event.getPacket().getHandle(), "adventure$content"); + + if (adventureContent != null) { + final List contents = new ArrayList<>(); + + this.mergeChildren(adventureContent, contents); + final String mergedContents = String.join("", contents); + + return mergedContents; + } + + } else { final StructureModifier packet = event.getPacket().getModifier(); final StructureModifier chat = event.getPacket().getChatComponents(); @@ -325,6 +367,7 @@ else if (packet.size() > 1) { } } + // No components for this MC version else this.jsonMessage = event.getPacket().getStrings().read(0); @@ -349,6 +392,24 @@ else if (packet.size() > 1) { return ""; } + /* + * Helper method to get content of all children of the given component + */ + private void mergeChildren(Object component, List contents) { + final Method contentMethod = ReflectionUtil.getMethod(component.getClass(), "content"); + final Method childrenMethod = ReflectionUtil.getMethod(component.getClass(), "children"); + + if (textComponentClass == null) + textComponentClass = ReflectionUtil.lookupClass("net.kyori.adventure.text.TextComponent"); + + if (textComponentClass.isAssignableFrom(component.getClass())) { + contents.add(ReflectionUtil.invoke(contentMethod, component)); + + for (Object child : (List) ReflectionUtil.invoke(childrenMethod, component)) + mergeChildren(child, contents); + } + } + /* * Writes the edited message as JSON format from the event */ @@ -357,9 +418,15 @@ private void writeEditedMessage(String message, PacketEvent event) { this.jsonMessage = Remain.toJson(message); - if (this.systemChat) - event.getPacket().getStrings().writeSafely(0, this.jsonMessage); - else if (this.isBaseComponent) + if (this.systemChat) { + + // Need to cancel due to Record class having final fields and write our own packet, so much for simplicity of Adventure-Paper impl + event.setCancelled(true); + + // We simply send a new packet instead + sendSystemChatPacket(this.player, message); + + } else if (this.isBaseComponent) packet.writeSafely(this.adventure ? 2 : 1, Remain.toComponent(this.jsonMessage)); else if (MinecraftVersion.atLeast(V.v1_7))