diff --git a/src/main/java/buttondevteam/chat/ChatProcessing.java b/src/main/java/buttondevteam/chat/ChatProcessing.java index 9bb084e..9532163 100644 --- a/src/main/java/buttondevteam/chat/ChatProcessing.java +++ b/src/main/java/buttondevteam/chat/ChatProcessing.java @@ -20,11 +20,7 @@ import com.palmergames.bukkit.towny.object.Town; import buttondevteam.chat.commands.UnlolCommand; import buttondevteam.chat.commands.ucmds.admin.DebugCommand; -import buttondevteam.chat.formatting.ChatFormatter; -import buttondevteam.chat.formatting.ChatFormatterBuilder; -import buttondevteam.chat.formatting.TellrawEvent; -import buttondevteam.chat.formatting.TellrawPart; -import buttondevteam.chat.formatting.TellrawSerializer; +import buttondevteam.chat.formatting.*; import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCPlayer; import buttondevteam.lib.chat.Channel; @@ -33,19 +29,51 @@ import buttondevteam.chat.listener.PlayerListener; import buttondevteam.lib.chat.*; public class ChatProcessing { - private static final Pattern ESCAPE_PATTERN = Pattern.compile("\\\\([\\*\\_\\\\])"); + private static final Pattern NULL_MENTION_PATTERN = Pattern.compile("null"); + private static final Pattern ESCAPE_PATTERN = Pattern.compile("\\\\"); private static final Pattern CONSOLE_PING_PATTERN = Pattern.compile("(?i)" + Pattern.quote("@console")); private static final Pattern HASHTAG_PATTERN = Pattern.compile("#(\\w+)"); private static final Pattern URL_PATTERN = Pattern.compile("(http[\\w:/?=$\\-_.+!*'(),]+)"); private static final Pattern ENTIRE_MESSAGE_PATTERN = Pattern.compile(".+"); - private static final Pattern UNDERLINED_PATTERN = Pattern.compile("(? commonFormatters = new ArrayList<>(); + + public static final ChatFormatter ESCAPE_FORMATTER = new ChatFormatterBuilder().setRegex(ESCAPE_PATTERN).build(); + + private ChatProcessing() { + } + + static { + commonFormatters.add(new ChatFormatterBuilder().setRegex(BOLD_PATTERN).setFormat(Format.Bold) + .setRemoveCharCount((short) 2).setRange(true).build()); + commonFormatters.add(new ChatFormatterBuilder().setRegex(ITALIC_PATTERN).setFormat(Format.Italic) + .setRemoveCharCount((short) 1).setRange(true).build()); + commonFormatters.add(new ChatFormatterBuilder().setRegex(UNDERLINED_PATTERN).setFormat(Format.Underlined) + .setRemoveCharCount((short) 1).setRange(true).build()); + commonFormatters.add(ESCAPE_FORMATTER); + // URLs + Rainbow text + commonFormatters.add(new ChatFormatterBuilder().setRegex(URL_PATTERN).setFormat(Format.Underlined) + .setOpenlink("$1").setRange(true).build()); + commonFormatters.add(new ChatFormatterBuilder().setRegex(NULL_MENTION_PATTERN).setColor(Color.DarkRed).build()); // Properly added a bug as a feature + commonFormatters.add(new ChatFormatterBuilder().setRegex(CONSOLE_PING_PATTERN).setColor(Color.Aqua) + .setOnmatch((String match) -> { + if (!pingedconsole) { + System.out.print("\007"); + pingedconsole = true; // Will set it to false in ProcessChat + } + return match; + }).setPriority(Priority.High).build()); + + commonFormatters.add(new ChatFormatterBuilder().setRegex(HASHTAG_PATTERN).setColor(Color.Blue) + .setOpenlink("https://twitter.com/hashtag/$1").setPriority(Priority.High).build()); + } + // Returns e.setCancelled for custom event public static boolean ProcessChat(Channel channel, CommandSender sender, String message) { long processstart = System.nanoTime(); @@ -78,7 +106,8 @@ public class ChatProcessing { } Channel currentchannel = channel; - ArrayList formatters = new ArrayList(); + @SuppressWarnings("unchecked") + ArrayList formatters = (ArrayList) commonFormatters.clone(); Color colormode = currentchannel.color; if (mp != null && mp.OtherColorMode != null) @@ -96,17 +125,6 @@ public class ChatProcessing { String suggestmsg = formattedmessage; - formatters.add(new ChatFormatterBuilder().setRegex(BOLD_PATTERN).setFormat(Format.Bold) - .setRemoveCharCount((short) 2).build()); - formatters.add(new ChatFormatterBuilder().setRegex(ITALIC_PATTERN).setFormat(Format.Italic) - .setRemoveCharCount((short) 1).build()); - formatters.add(new ChatFormatterBuilder().setRegex(UNDERLINED_PATTERN).setFormat(Format.Underlined) - .setRemoveCharCount((short) 1).build()); - formatters.add(new ChatFormatterBuilder().setRegex(ESCAPE_PATTERN).setRemoveCharPos((short) 0).build()); - - // URLs + Rainbow text - formatters.add(new ChatFormatterBuilder().setRegex(URL_PATTERN).setFormat(Format.Underlined).setOpenlink("$1") - .build()); if (Bukkit.getOnlinePlayers().size() > 0) { StringBuilder namesb = new StringBuilder(); namesb.append("(?i)("); @@ -116,6 +134,7 @@ public class ChatProcessing { namesb.append(")"); StringBuilder nicksb = new StringBuilder(); nicksb.append("(?i)("); + boolean addNickFormatter = false; { final int size = Bukkit.getOnlinePlayers().size(); int index = 0; @@ -125,6 +144,7 @@ public class ChatProcessing { nicksb.append(nick); if (index < size - 1) { nicksb.append("|"); + addNickFormatter = true; } } index++; @@ -132,9 +152,6 @@ public class ChatProcessing { nicksb.append(")"); } - formatters - .add(new ChatFormatterBuilder().setRegex(Pattern.compile("null")).setColor(Color.DarkRed).build()); // Properly added a bug as a feature - formatters.add(new ChatFormatterBuilder().setRegex(Pattern.compile(namesb.toString())).setColor(Color.Aqua) .setOnmatch((String match) -> { Player p = Bukkit.getPlayer(match); @@ -153,61 +170,46 @@ public class ChatProcessing { return color + p.getName() + "§r"; }).setPriority(Priority.High).build()); - formatters.add(new ChatFormatterBuilder().setRegex(Pattern.compile(nicksb.toString())).setColor(Color.Aqua) - .setOnmatch((String match) -> { - if (PlayerListener.nicknames.containsKey(match)) { - Player p = Bukkit.getPlayer(PlayerListener.nicknames.get(match)); - if (p == null) { - PluginMain.Instance.getLogger().warning( - "Error: Can't find player nicknamed " + match + " but was reported as online."); - return "§c" + match + "§r"; + if (addNickFormatter) + formatters.add(new ChatFormatterBuilder().setRegex(Pattern.compile(nicksb.toString())) + .setColor(Color.Aqua).setOnmatch((String match) -> { + if (PlayerListener.nicknames.containsKey(match)) { + Player p = Bukkit.getPlayer(PlayerListener.nicknames.get(match)); + if (p == null) { + PluginMain.Instance.getLogger().warning("Error: Can't find player nicknamed " + + match + " but was reported as online."); + return "§c" + match + "§r"; + } + if (PlayerListener.NotificationSound == null) + p.playSound(p.getLocation(), Sound.ENTITY_ARROW_HIT_PLAYER, 1.0f, 0.5f); + else + p.playSound(p.getLocation(), PlayerListener.NotificationSound, 1.0f, + (float) PlayerListener.NotificationPitch); + return PluginMain.essentials.getUser(p).getNickname(); } - if (PlayerListener.NotificationSound == null) - p.playSound(p.getLocation(), Sound.ENTITY_ARROW_HIT_PLAYER, 1.0f, 0.5f); - else - p.playSound(p.getLocation(), PlayerListener.NotificationSound, 1.0f, - (float) PlayerListener.NotificationPitch); - return PluginMain.essentials.getUser(p).getNickname(); - } - Bukkit.getServer().getLogger().warning( - "Player nicknamed " + match + " not found in nickname map but was reported as online."); - return "§c" + match + "§r"; - }).setPriority(Priority.High).build()); + Bukkit.getServer().getLogger().warning("Player nicknamed " + match + + " not found in nickname map but was reported as online."); + return "§c" + match + "§r"; + }).setPriority(Priority.High).build()); } - pingedconsole = false; - formatters.add(new ChatFormatterBuilder().setRegex(CONSOLE_PING_PATTERN).setColor(Color.Aqua) - .setOnmatch((String match) -> { - if (!pingedconsole) { - System.out.print("\007"); - pingedconsole = true; - } - return match; - }).setPriority(Priority.High).build()); - - formatters.add(new ChatFormatterBuilder().setRegex(HASHTAG_PATTERN).setColor(Color.Blue) - .setOpenlink("https://twitter.com/hashtag/$1").setPriority(Priority.High).build()); - - /* - * if (!hadurls) { if (formattedmessage.matches("(?i).*" + Pattern.quote("@console") + ".*")) { formattedmessage = formattedmessage.replaceAll( "(?i)" + Pattern.quote("@console"), - * "§b@console§r"); formattedmessage = formattedmessage .replaceAll( "(?i)" + Pattern.quote("@console"), String.format( - * "\",\"color\":\"%s\"},{\"text\":\"§b@console§r\",\"color\":\"blue\"},{\"text\":\"" , colormode)); System.out.println("\007"); } } - */ + pingedconsole = false; // Will set it to true onmatch (static constructor) TellrawPart json = new TellrawPart(""); if (mp != null && mp.ChatOnly) { json.addExtra(new TellrawPart("[C]").setHoverEvent( TellrawEvent.create(TellrawEvent.HoverAC, TellrawEvent.HoverAction.SHOW_TEXT, "Chat only"))); } - final String channelidentifier = ("[" + (sender instanceof IDiscordSender ? "d|" : "") + currentchannel.DisplayName) - + "]" + (mp != null && !mp.RPMode ? "[OOC]" : ""); + final String channelidentifier = ("[" + (sender instanceof IDiscordSender ? "d|" : "") + + currentchannel.DisplayName) + "]" + (mp != null && !mp.RPMode ? "[OOC]" : ""); json.addExtra( - new TellrawPart(channelidentifier).setHoverEvent( + new TellrawPart(channelidentifier) + .setHoverEvent( TellrawEvent.create(TellrawEvent.HoverAC, TellrawEvent.HoverAction.SHOW_TEXT, new TellrawPart((sender instanceof IDiscordSender ? "From Discord\n" : "") + "Copy message").setColor(Color.Blue))) - .setClickEvent(TellrawEvent.create(TellrawEvent.ClickAC, - TellrawEvent.ClickAction.SUGGEST_COMMAND, suggestmsg))); + .setClickEvent(TellrawEvent.create(TellrawEvent.ClickAC, + TellrawEvent.ClickAction.SUGGEST_COMMAND, suggestmsg))); json.addExtra(new TellrawPart(" <")); json.addExtra( new TellrawPart( @@ -382,9 +384,8 @@ public class ChatProcessing { player.sendMessage("§cAn error occured while sending the message."); return true; } - PluginMain.Instance.getServer().getConsoleSender() - .sendMessage(String.format("[%s] <%s> %s", channelidentifier, - (player != null ? player.getDisplayName() : sender.getName()), message)); + PluginMain.Instance.getServer().getConsoleSender().sendMessage(String.format("%s <%s> %s", channelidentifier, + (player != null ? player.getDisplayName() : sender.getName()), message)); DebugCommand.SendDebugMessage( "-- Full ChatProcessing time: " + (System.nanoTime() - processstart) / 1000000f + " ms"); DebugCommand.SendDebugMessage("-- ChatFormatter.Combine time: " + combinetime / 1000000f + " ms"); diff --git a/src/main/java/buttondevteam/chat/formatting/ChatFormatter.java b/src/main/java/buttondevteam/chat/formatting/ChatFormatter.java index 0594c93..ce58598 100644 --- a/src/main/java/buttondevteam/chat/formatting/ChatFormatter.java +++ b/src/main/java/buttondevteam/chat/formatting/ChatFormatter.java @@ -2,11 +2,14 @@ package buttondevteam.chat.formatting; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; +import buttondevteam.chat.ChatProcessing; import buttondevteam.chat.commands.ucmds.admin.DebugCommand; import buttondevteam.lib.chat.*; @@ -19,9 +22,10 @@ public final class ChatFormatter { private Priority priority; private short removecharcount = 0; private short removecharpos = -1; + private boolean isrange; public ChatFormatter(Pattern regex, Format format, Color color, Function onmatch, String openlink, - Priority priority, short removecharcount, short removecharpos) { + Priority priority, short removecharcount, short removecharpos, boolean isrange) { this.regex = regex; this.format = format; this.color = color; @@ -30,6 +34,7 @@ public final class ChatFormatter { this.priority = Priority.High; this.removecharcount = removecharcount; this.removecharpos = removecharpos; + this.isrange = isrange; } public static void Combine(List formatters, String str, TellrawPart tp) { @@ -48,7 +53,8 @@ public final class ChatFormatter { if (groups.size() > 0) DebugCommand.SendDebugMessage("First group: " + groups.get(0)); FormattedSection section = new FormattedSection(formatter, matcher.start(), matcher.end() - 1, groups, - formatter.removecharcount, formatter.removecharcount, formatter.removecharpos); + formatter.removecharcount, formatter.removecharcount, formatter.removecharpos, + formatter.isrange); sections.add(section); } } @@ -58,6 +64,37 @@ public final class ChatFormatter { else return Integer.compare(s1.Start, s2.Start); }); + ArrayList combined = new ArrayList<>(); + Map nextSection = new HashMap<>(); + boolean escaped = false; + for (int i = 0; i < sections.size(); i++) { + // Set ending to -1 until closed with another 1 long "section" - only do this if IsRange is true + final FormattedSection section = sections.get(i); + if (!section.IsRange) { + escaped = section.Formatters.contains(ChatProcessing.ESCAPE_FORMATTER) && !escaped; // Enable escaping on first \, disable on second + if (escaped) // Don't add the escape character + section.RemCharFromStart = 1; + combined.add(section); + DebugCommand.SendDebugMessage("Added " + (!escaped ? "not " : "") + "escaped section: " + section); + continue; + } + if (!escaped) { + if (nextSection.containsKey(section.Formatters.get(0))) { + FormattedSection s = nextSection.remove(section.Formatters.get(0)); + s.End = section.Start; + s.IsRange = false; // IsRange means it's a 1 long section indicating a start or an end + combined.add(s); + DebugCommand.SendDebugMessage("Finished section: " + s); + } else { + DebugCommand.SendDebugMessage("Adding next section: " + section); + nextSection.put(section.Formatters.get(0), section); + } + } else { + DebugCommand.SendDebugMessage("Skipping section: " + section); + escaped = false; // Reset escaping if applied, like if we're at the '*' in '\*' + } + } + sections = combined; boolean cont = true; boolean found = false; for (int i = 1; cont;) { @@ -90,7 +127,7 @@ public final class ChatFormatter { } FormattedSection section = new FormattedSection(firstSection.Formatters, sections.get(i).Start, origend, firstSection.Matches, sections.get(i).RemCharFromStart, firstSection.RemCharFromEnd, - Collections.emptyList()); + Collections.emptyList(), false); section.Formatters.addAll(sections.get(i).Formatters); section.Matches.addAll(sections.get(i).Matches); // TODO: Clean sections.add(i, section); @@ -169,7 +206,7 @@ public final class ChatFormatter { if (formatter.color != null) color = formatter.color; if (formatter.format != null) - format = formatter.format.getFlag(); //TODO: Fix + format = formatter.format.getFlag(); // TODO: Fix if (formatter.openlink != null) openlink = formatter.openlink; } @@ -192,6 +229,6 @@ public final class ChatFormatter { @Override public String toString() { return new StringBuilder("F(").append(color).append(", ").append(format).append(", ").append(openlink) - .append(", ").append(priority).append(")").toString(); + .append(", ").append(priority).append(", ").append(regex).append(")").toString(); } } diff --git a/src/main/java/buttondevteam/chat/formatting/ChatFormatterBuilder.java b/src/main/java/buttondevteam/chat/formatting/ChatFormatterBuilder.java index 2277b10..a835d7f 100644 --- a/src/main/java/buttondevteam/chat/formatting/ChatFormatterBuilder.java +++ b/src/main/java/buttondevteam/chat/formatting/ChatFormatterBuilder.java @@ -14,9 +14,11 @@ public class ChatFormatterBuilder { private Priority priority; private short removecharcount = 0; private short removecharpos = -1; + private boolean range = false; public ChatFormatter build() { - return new ChatFormatter(regex, format, color, onmatch, openlink, priority, removecharcount, removecharpos); + return new ChatFormatter(regex, format, color, onmatch, openlink, priority, removecharcount, removecharpos, + range); } public Pattern getRegex() { @@ -100,4 +102,13 @@ public class ChatFormatterBuilder { this.removecharpos = removecharpos; return this; } + + public boolean isRange() { + return range; + } + + public ChatFormatterBuilder setRange(boolean range) { + this.range = range; + return this; + } } diff --git a/src/main/java/buttondevteam/chat/formatting/FormattedSection.java b/src/main/java/buttondevteam/chat/formatting/FormattedSection.java index 4d2f209..2891d21 100644 --- a/src/main/java/buttondevteam/chat/formatting/FormattedSection.java +++ b/src/main/java/buttondevteam/chat/formatting/FormattedSection.java @@ -11,9 +11,10 @@ class FormattedSection { short RemCharFromStart; short RemCharFromEnd; ArrayList RemCharPos = new ArrayList(); + boolean IsRange; FormattedSection(ChatFormatter formatter, int start, int end, ArrayList matches, short remcharfromstart, - short remcharfromend, int remcharpos) { + short remcharfromend, int remcharpos, boolean isrange) { Start = start; End = end; Formatters.add(formatter); @@ -21,10 +22,11 @@ class FormattedSection { RemCharFromStart = remcharfromstart; RemCharFromEnd = remcharfromend; RemCharPos.add(remcharpos); + IsRange = isrange; } FormattedSection(Collection formatters, int start, int end, ArrayList matches, - short remcharfromstart, short remcharfromend, Collection remcharpos) { + short remcharfromstart, short remcharfromend, Collection remcharpos, boolean isrange) { Start = start; End = end; Formatters.addAll(formatters); @@ -32,6 +34,7 @@ class FormattedSection { RemCharFromStart = remcharfromstart; RemCharFromEnd = remcharfromend; RemCharPos.addAll(remcharpos); + IsRange = isrange; } @Override @@ -39,6 +42,6 @@ class FormattedSection { return new StringBuilder("Section(").append(Start).append(", ").append(End).append(", formatters: ") .append(Formatters.toString()).append(", matches: ").append(Matches.toString()).append(", RemChars: ") .append(RemCharFromStart).append(", ").append(RemCharFromEnd).append(", ").append(RemCharPos) - .append(")").toString(); + .append(", ").append(IsRange).append(")").toString(); } } \ No newline at end of file