311 lines
12 KiB
Java
311 lines
12 KiB
Java
package com.songoda.core.chat;
|
|
|
|
import com.google.gson.Gson;
|
|
import com.google.gson.GsonBuilder;
|
|
import com.google.gson.JsonObject;
|
|
import com.songoda.core.compatibility.ClassMapping;
|
|
import com.songoda.core.compatibility.ServerVersion;
|
|
import com.songoda.core.nms.Nms;
|
|
import com.songoda.core.utils.TextUtils;
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.command.CommandSender;
|
|
import org.bukkit.entity.Player;
|
|
|
|
import java.lang.reflect.Constructor;
|
|
import java.lang.reflect.Field;
|
|
import java.lang.reflect.Method;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.UUID;
|
|
import java.util.logging.Level;
|
|
import java.util.regex.Matcher;
|
|
import java.util.regex.Pattern;
|
|
|
|
public class ChatMessage {
|
|
private static final Gson gson = new GsonBuilder().create();
|
|
private final List<JsonObject> textList = new ArrayList<>();
|
|
|
|
public void clear() {
|
|
textList.clear();
|
|
}
|
|
|
|
public ChatMessage fromText(String text) {
|
|
return fromText(text, false);
|
|
}
|
|
|
|
public ChatMessage fromText(String text, boolean noHex) {
|
|
Pattern pattern = Pattern.compile(
|
|
"(.*?)(?!&([omnlk]))(?=(&([123456789abcdefr#])|$)|#([a-f]|[A-F]|[0-9]){6})",
|
|
Pattern.CASE_INSENSITIVE);
|
|
Matcher matcher = pattern.matcher(text);
|
|
|
|
while (matcher.find()) {
|
|
ColorContainer color = null;
|
|
String match1 = matcher.group(1);
|
|
|
|
if (matcher.groupCount() == 0 || match1.length() == 0) {
|
|
continue;
|
|
}
|
|
|
|
char colorChar = '-';
|
|
|
|
if (matcher.start() != 0) {
|
|
colorChar = text.substring(matcher.start() - 1, matcher.start()).charAt(0);
|
|
}
|
|
|
|
if (colorChar != '-') {
|
|
if (colorChar == '#') {
|
|
color = new ColorContainer(match1.substring(0, 6), noHex);
|
|
match1 = match1.substring(5);
|
|
} else if (colorChar == '&') {
|
|
color = new ColorContainer(ColorCode.getByChar(Character.toLowerCase(match1.charAt(0))));
|
|
}
|
|
}
|
|
|
|
Pattern subPattern = Pattern.compile("(.*?)(?=&([omnlk])|$)");
|
|
Matcher subMatcher = subPattern.matcher(match1);
|
|
|
|
List<ColorCode> stackedCodes = new ArrayList<>();
|
|
while (subMatcher.find()) {
|
|
String match2 = subMatcher.group(1);
|
|
if (match2.length() == 0) continue;
|
|
|
|
ColorCode code = ColorCode.getByChar(Character.toLowerCase(match2.charAt(0)));
|
|
|
|
if (code != null && code != ColorCode.RESET) {
|
|
stackedCodes.add(code);
|
|
}
|
|
|
|
if (color != null) {
|
|
match2 = match2.substring(1);
|
|
}
|
|
|
|
if (match2.length() != 0) {
|
|
addMessage(match2, color, stackedCodes);
|
|
}
|
|
}
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
public String toText() {
|
|
return toText(false);
|
|
}
|
|
|
|
public String toText(boolean noHex) {
|
|
StringBuilder text = new StringBuilder();
|
|
|
|
for (JsonObject object : textList) {
|
|
if (object.has("color")) {
|
|
String color = object.get("color").getAsString();
|
|
text.append("&");
|
|
|
|
if (color.length() == 7) {
|
|
text.append(new ColorContainer(color, noHex).getColor().getCode());
|
|
} else {
|
|
text.append(ColorCode.valueOf(color.toUpperCase()).getCode());
|
|
}
|
|
}
|
|
|
|
for (ColorCode code : ColorCode.values()) {
|
|
if (code.isColor()) continue;
|
|
|
|
String c = code.name().toLowerCase();
|
|
if (object.has(c) && object.get(c).getAsBoolean()) {
|
|
text.append("&").append(code.getCode());
|
|
}
|
|
}
|
|
|
|
text.append(object.get("text").getAsString());
|
|
}
|
|
|
|
return text.toString();
|
|
}
|
|
|
|
public ChatMessage addMessage(String s) {
|
|
JsonObject txt = new JsonObject();
|
|
txt.addProperty("text", s);
|
|
|
|
textList.add(txt);
|
|
|
|
return this;
|
|
}
|
|
|
|
public ChatMessage addMessage(String text, ColorContainer color) {
|
|
return addMessage(text, color, Collections.emptyList());
|
|
}
|
|
|
|
public ChatMessage addMessage(String text, ColorContainer color, List<ColorCode> colorCodes) {
|
|
JsonObject txt = new JsonObject();
|
|
txt.addProperty("text", text);
|
|
|
|
if (color != null) {
|
|
txt.addProperty("color", color.getHexCode() != null ? "#" + color.getHexCode() : color.getColorCode().name().toLowerCase());
|
|
}
|
|
|
|
for (ColorCode code : ColorCode.values()) {
|
|
if (!code.isColor()) {
|
|
txt.addProperty(code.name().toLowerCase(), colorCodes.contains(code));
|
|
}
|
|
}
|
|
|
|
textList.add(txt);
|
|
return this;
|
|
}
|
|
|
|
public ChatMessage addRunCommand(String text, String hoverText, String cmd) {
|
|
JsonObject txt = new JsonObject();
|
|
txt.addProperty("text", text);
|
|
|
|
JsonObject hover = new JsonObject();
|
|
hover.addProperty("action", "show_text");
|
|
hover.addProperty("value", hoverText);
|
|
txt.add("hoverEvent", hover);
|
|
|
|
JsonObject click = new JsonObject();
|
|
click.addProperty("action", "run_command");
|
|
click.addProperty("value", cmd);
|
|
txt.add("clickEvent", click);
|
|
|
|
textList.add(txt);
|
|
return this;
|
|
}
|
|
|
|
public ChatMessage addPromptCommand(String text, String hoverText, String cmd) {
|
|
JsonObject txt = new JsonObject();
|
|
txt.addProperty("text", text);
|
|
|
|
JsonObject hover = new JsonObject();
|
|
hover.addProperty("action", "show_text");
|
|
hover.addProperty("value", hoverText);
|
|
txt.add("hoverEvent", hover);
|
|
|
|
JsonObject click = new JsonObject();
|
|
click.addProperty("action", "suggest_command");
|
|
click.addProperty("value", cmd);
|
|
txt.add("clickEvent", click);
|
|
|
|
textList.add(txt);
|
|
return this;
|
|
}
|
|
|
|
public ChatMessage addURL(String text, String hoverText, String url) {
|
|
JsonObject txt = new JsonObject();
|
|
txt.addProperty("text", text);
|
|
|
|
JsonObject hover = new JsonObject();
|
|
hover.addProperty("action", "show_text");
|
|
hover.addProperty("value", hoverText);
|
|
txt.add("hoverEvent", hover);
|
|
|
|
JsonObject click = new JsonObject();
|
|
click.addProperty("action", "open_url");
|
|
click.addProperty("value", url);
|
|
txt.add("clickEvent", hover);
|
|
|
|
textList.add(txt);
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return gson.toJson(textList);
|
|
}
|
|
|
|
public void sendTo(CommandSender sender) {
|
|
sendTo(null, sender);
|
|
}
|
|
|
|
public void sendTo(ChatMessage prefix, CommandSender sender) {
|
|
if (sender instanceof Player && enabled) {
|
|
try {
|
|
List<JsonObject> textList = prefix == null ? new ArrayList<>() : new ArrayList<>(prefix.textList);
|
|
textList.addAll(this.textList);
|
|
|
|
Object packet;
|
|
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_19)) {
|
|
packet = mc_PacketPlayOutChat_new.newInstance(mc_IChatBaseComponent_ChatSerializer_a.invoke(null, gson.toJson(textList)), mc_PacketPlayOutChat_new_1_19_0 ? 1 : false);
|
|
} else if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_16)) {
|
|
packet = mc_PacketPlayOutChat_new.newInstance(
|
|
mc_IChatBaseComponent_ChatSerializer_a.invoke(null, gson.toJson(textList)),
|
|
mc_chatMessageType_Chat.get(null),
|
|
((Player) sender).getUniqueId());
|
|
} else {
|
|
packet = mc_PacketPlayOutChat_new.newInstance(mc_IChatBaseComponent_ChatSerializer_a.invoke(null, gson.toJson(textList)));
|
|
}
|
|
|
|
Nms.getImplementations().getPlayer().sendPacket((Player) sender, packet);
|
|
} catch (ReflectiveOperationException | IllegalArgumentException ex) {
|
|
Bukkit.getLogger().log(Level.WARNING, "Problem preparing raw chat packets (disabling further packets)", ex);
|
|
enabled = false;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
sender.sendMessage(TextUtils.formatText((prefix == null ? "" : prefix.toText(true) + " ") + toText(true)));
|
|
}
|
|
|
|
private static boolean enabled = ServerVersion.isServerVersionAtLeast(ServerVersion.V1_8);
|
|
|
|
private static Class<?> mc_ChatMessageType;
|
|
private static Method mc_IChatBaseComponent_ChatSerializer_a, cb_craftPlayer_getHandle;
|
|
private static Constructor mc_PacketPlayOutChat_new;
|
|
private static Field mc_entityPlayer_playerConnection, mc_chatMessageType_Chat;
|
|
private static boolean mc_PacketPlayOutChat_new_1_19_0 = false;
|
|
|
|
static {
|
|
init();
|
|
}
|
|
|
|
static void init() {
|
|
if (enabled) {
|
|
try {
|
|
final String version = ServerVersion.getServerVersionString();
|
|
Class<?> cb_craftPlayerClazz, mc_entityPlayerClazz,
|
|
mc_IChatBaseComponent, mc_IChatBaseComponent_ChatSerializer, mc_PacketPlayOutChat;
|
|
|
|
cb_craftPlayerClazz = ClassMapping.CRAFT_PLAYER.getClazz();
|
|
cb_craftPlayer_getHandle = cb_craftPlayerClazz.getDeclaredMethod("getHandle");
|
|
mc_entityPlayerClazz = ClassMapping.ENTITY_PLAYER.getClazz();
|
|
mc_entityPlayer_playerConnection = mc_entityPlayerClazz.getDeclaredField(ServerVersion.isServerVersionAtLeast(ServerVersion.V1_17) ? "b" : "playerConnection");
|
|
mc_IChatBaseComponent = ClassMapping.I_CHAT_BASE_COMPONENT.getClazz();
|
|
mc_IChatBaseComponent_ChatSerializer = ClassMapping.I_CHAT_BASE_COMPONENT.getClazz("ChatSerializer");
|
|
mc_IChatBaseComponent_ChatSerializer_a = mc_IChatBaseComponent_ChatSerializer.getMethod("a", String.class);
|
|
mc_PacketPlayOutChat = ServerVersion.isServerVersionAtLeast(ServerVersion.V1_19) ? ClassMapping.CLIENTBOUND_SYSTEM_CHAT.getClazz() : ClassMapping.PACKET_PLAY_OUT_CHAT.getClazz();
|
|
|
|
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_19)) {
|
|
try {
|
|
mc_PacketPlayOutChat_new = mc_PacketPlayOutChat.getConstructor(mc_IChatBaseComponent, Boolean.TYPE);
|
|
} catch (NoSuchMethodException ex) {
|
|
mc_PacketPlayOutChat_new = mc_PacketPlayOutChat.getConstructor(mc_IChatBaseComponent, Integer.TYPE);
|
|
mc_PacketPlayOutChat_new_1_19_0 = true;
|
|
}
|
|
} else if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_16)) {
|
|
mc_ChatMessageType = ClassMapping.CHAT_MESSAGE_TYPE.getClazz();
|
|
mc_chatMessageType_Chat = mc_ChatMessageType.getField(ServerVersion.isServerVersionAtLeast(ServerVersion.V1_17) ? "a" : "CHAT");
|
|
mc_PacketPlayOutChat_new = mc_PacketPlayOutChat.getConstructor(mc_IChatBaseComponent, mc_ChatMessageType, UUID.class);
|
|
} else {
|
|
mc_PacketPlayOutChat_new = mc_PacketPlayOutChat.getConstructor(mc_IChatBaseComponent);
|
|
}
|
|
} catch (Throwable ex) {
|
|
Bukkit.getLogger().log(Level.WARNING, "Problem preparing raw chat packets (disabling further packets)", ex);
|
|
enabled = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
public ChatMessage replaceAll(String toReplace, String replaceWith) {
|
|
for (JsonObject object : textList) {
|
|
String text = object.get("text").getAsString().replaceAll(toReplace, replaceWith);
|
|
|
|
object.remove("text");
|
|
object.addProperty("text", text);
|
|
}
|
|
|
|
return this;
|
|
}
|
|
}
|