From f0b5d603adce2f2f148e18140d5a469b5e42759e Mon Sep 17 00:00:00 2001 From: John Etherfield Date: Sun, 4 Oct 2026 11:44:27 +0200 Subject: [PATCH] fix: give chat mentions default highlight color and support @tags --- src/main/java/org/examp/john/ChatHandler.java | 92 ++++++++++++++----- 1 file changed, 69 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/examp/john/ChatHandler.java b/src/main/java/org/examp/john/ChatHandler.java index 5c565d8..f96c139 100644 --- a/src/main/java/org/examp/john/ChatHandler.java +++ b/src/main/java/org/examp/john/ChatHandler.java @@ -17,6 +17,7 @@ import org.examp.john.stealth.StealthStorage; import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.Locale; public class ChatHandler { public static void register() { @@ -149,10 +150,11 @@ public class ChatHandler { while (!remaining.isEmpty()) { int earliestIndex = remaining.length(); ServerPlayer mentioned = null; + String lower = remaining.toLowerCase(Locale.ROOT); for (ServerPlayer player : sorted) { - String name = player.getName().getString(); - int index = remaining.toLowerCase().indexOf(name.toLowerCase()); + String nameLower = player.getName().getString().toLowerCase(Locale.ROOT); + int index = indexOfBoundary(lower, nameLower); if (index >= 0 && index < earliestIndex) { earliestIndex = index; mentioned = player; @@ -164,11 +166,13 @@ public class ChatHandler { break; } - if (earliestIndex > 0) { - result.append(Component.literal(remaining.substring(0, earliestIndex))); + boolean withAt = earliestIndex > 0 && remaining.charAt(earliestIndex - 1) == '@'; + int prefixEnd = withAt ? earliestIndex - 1 : earliestIndex; + if (prefixEnd > 0) { + result.append(Component.literal(remaining.substring(0, prefixEnd))); } - result.append(createMentionComponent(mentioned)); + result.append(createMentionComponent(mentioned, withAt)); remaining = remaining.substring(earliestIndex + mentioned.getName().getString().length()); } @@ -176,13 +180,32 @@ public class ChatHandler { return result; } - private static Component createMentionComponent(ServerPlayer player) { - String prefix = getLuckPermsPrefix(player); - String formattedPrefix = prefix.replace("&", "\u00a7"); - String name = player.getName().getString(); + private static int indexOfBoundary(String lowerHaystack, String lowerNeedle) { + int index = lowerHaystack.indexOf(lowerNeedle); + while (index >= 0) { + boolean okBefore = index == 0 || !isNameChar(lowerHaystack.charAt(index - 1)); + boolean okAfter = index + lowerNeedle.length() == lowerHaystack.length() + || !isNameChar(lowerHaystack.charAt(index + lowerNeedle.length())); + if (okBefore && okAfter) { + return index; + } + index = lowerHaystack.indexOf(lowerNeedle, index + 1); + } + return -1; + } - return Component.literal(formattedPrefix + name) + private static boolean isNameChar(char c) { + return Character.isLetterOrDigit(c) || c == '_'; + } + + private static Component createMentionComponent(ServerPlayer player, boolean withAt) { + String name = player.getName().getString(); + String prefix = formatLegacyPrefix(getLuckPermsPrefix(player)); + String text = (withAt ? "@" : "") + prefix + name; + + return Component.literal(text) .withStyle(Style.EMPTY + .withColor(ChatFormatting.AQUA) .withClickEvent(new ClickEvent.SuggestCommand("/msg " + name + " ")) .withHoverEvent(new HoverEvent.ShowText( Component.literal("Натисніть щоб написати " + name) @@ -190,6 +213,40 @@ public class ChatHandler { ); } + private static String formatLegacyPrefix(String raw) { + if (raw == null || raw.isEmpty()) { + return ""; + } + String s = raw.replace('&', '§'); + StringBuilder out = new StringBuilder(s.length() + 14); + int i = 0; + while (i < s.length()) { + if (s.charAt(i) == '§' && i + 8 <= s.length() && s.charAt(i + 1) == '#' + && isSixHex(s, i + 2)) { + out.append("§x"); + for (int j = 2; j < 8; j++) { + out.append('§').append(s.charAt(i + j)); + } + i += 8; + } else { + out.append(s.charAt(i)); + i++; + } + } + return out.toString(); + } + + private static boolean isSixHex(String s, int start) { + for (int i = start; i < start + 6; i++) { + char c = s.charAt(i); + boolean hex = (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); + if (!hex) { + return false; + } + } + return true; + } + private static String getLuckPermsPrefix(ServerPlayer player) { try { LuckPerms luckPerms = LuckPermsProvider.get(); @@ -206,21 +263,10 @@ public class ChatHandler { } private static MutableComponent buildFormattedName(ServerPlayer player) { - String prefix = ""; - try { - LuckPerms luckPerms = LuckPermsProvider.get(); - User user = luckPerms.getUserManager().getUser(player.getUUID()); - if (user != null) { - String metaPrefix = user.getCachedData().getMetaData().getPrefix(); - if (metaPrefix != null) { - prefix = metaPrefix; - } - } - } catch (Throwable ignored) { - } + String prefix = formatLegacyPrefix(getLuckPermsPrefix(player)); return Component.empty() - .append(Component.literal(prefix.replace("&", "§"))) + .append(Component.literal(prefix)) .append(player.getDisplayName()); } }