From 4d0c7c170e2b7f48e5981f7a2fb38cdebf1ee480 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Thu, 13 Feb 2020 00:24:31 +0100 Subject: [PATCH] Fix many issues with the chat processing #71 --- .../components/formatter/ChatProcessing.java | 24 +++++++------- .../formatter/formatting/ChatFormatUtils.java | 6 +++- .../formatter/formatting/ChatFormatter.java | 30 +++++++++++------ .../formatter/formatting/FormatSettings.java | 12 ++++--- .../formatting/FormattedSection.java | 3 +- .../formatter/formatting/MatchProvider.java | 5 +++ .../formatting/MatchProviderBase.java | 7 ++++ .../formatting/RangeMatchProvider.java | 32 +++++++++++++------ .../formatting/RegexMatchProvider.java | 8 ++++- .../formatting/StringMatchProvider.java | 21 +++++++++++- 10 files changed, 109 insertions(+), 39 deletions(-) diff --git a/src/main/java/buttondevteam/chat/components/formatter/ChatProcessing.java b/src/main/java/buttondevteam/chat/components/formatter/ChatProcessing.java index d6b955e..fc94817 100644 --- a/src/main/java/buttondevteam/chat/components/formatter/ChatProcessing.java +++ b/src/main/java/buttondevteam/chat/components/formatter/ChatProcessing.java @@ -50,26 +50,26 @@ public class ChatProcessing { private static ArrayList commonFormatters = Lists.newArrayList( new RangeMatchProvider("bold", "**", FormatSettings.builder().bold(true).build()), new RangeMatchProvider("italic", "*", FormatSettings.builder().italic(true).build()), - new RangeMatchProvider("italic2", "_", FormatSettings.builder().italic(true).build()), new RangeMatchProvider("underlined", "__", FormatSettings.builder().underlined(true).build()), + new RangeMatchProvider("italic2", "_", FormatSettings.builder().italic(true).build()), new RangeMatchProvider("strikethrough", "~~", FormatSettings.builder().strikethrough(true).build()), new RangeMatchProvider("spoiler", "||", FormatSettings.builder().obfuscated(true) .onmatch((match, cf, fs) -> { cf.setHoverText(match); return match; }).build()), - new StringMatchProvider("nullMention", FormatSettings.builder().color(Color.DarkRed).build(), "null"), // Properly added a bug as a feature + new StringMatchProvider("nullMention", FormatSettings.builder().color(Color.DarkRed).build(), true, "null"), // Properly added a bug as a feature new StringMatchProvider("consolePing", FormatSettings.builder().color(Color.Aqua) .onmatch((match, builder, section) -> { if (!pingedconsole) { System.out.print("\007"); pingedconsole = true; // Will set it to false in ProcessChat } - return match; - }).build(), "@console"), + return "@console"; + }).build(), true, "@console"), new RegexMatchProvider("hashtag", HASHTAG_PATTERN, FormatSettings.builder().color(Color.Blue).openlink("https://twitter.com/hashtag/$1").build()), - new StringMatchProvider("cyan", FormatSettings.builder().color(Color.Aqua).build(), "cyan"), // #55 + new StringMatchProvider("cyan", FormatSettings.builder().color(Color.Aqua).build(), true, "cyan"), // #55 new RangeMatchProvider("code", "`", FormatSettings.builder().color(Color.DarkGray).build()), new RegexMatchProvider("maskedLink", MASKED_LINK_PATTERN, FormatSettings.builder().underlined(true) .onmatch((match, builder, section) -> { @@ -88,7 +88,7 @@ public class ChatProcessing { var player = players.get(playerC); playPingSound(player, ComponentManager.getIfEnabled(FormatterComponent.class)); return "@someone (" + player.getDisplayName() + "§r)"; - }).build())); + }).build(), true, "@someone")); private static Gson gson = new GsonBuilder() .registerTypeHierarchyAdapter(TellrawSerializableEnum.class, new TellrawSerializer.TwEnum()) .registerTypeHierarchyAdapter(Collection.class, new TellrawSerializer.TwCollection()) @@ -265,8 +265,8 @@ public class ChatProcessing { System.out.println(message); }; - if (names.length > 0) - formatters.add(new StringMatchProvider("name", FormatSettings.builder().color(Color.Aqua) + if (names.length > 0) //Add as first so it handles special characters (_) - TODO: But after URLs + formatters.add(0, new StringMatchProvider("name", FormatSettings.builder().color(Color.Aqua) .onmatch((match, builder, section) -> { Player p = Bukkit.getPlayer(match); Optional pn = nottest ? Optional.empty() @@ -281,10 +281,10 @@ public class ChatProcessing { } String color = String.format("§%x", (mpp.GetFlairColor() == 0x00 ? 0xb : mpp.GetFlairColor())); return color + (nottest ? p.getName() : pn.get()) + "§r"; //Fix name casing, except when testing - }).build(), names)); + }).build(), true, names)); - if (nicknames.length > 0) - formatters.add(new StringMatchProvider("nickname", FormatSettings.builder().color(Color.Aqua) + if (nicknames.length > 0) //Add as first so it handles special characters + formatters.add(0, new StringMatchProvider("nickname", FormatSettings.builder().color(Color.Aqua) .onmatch((match, builder, section) -> { if (PlayerListener.nicknames.containsKey(match.toLowerCase())) { //Made a stream and all that but I can actually store it lowercased Player p = Bukkit.getPlayer(PlayerListener.nicknames.get(match.toLowerCase())); @@ -299,7 +299,7 @@ public class ChatProcessing { error.accept("Player nicknamed " + match.toLowerCase() + " not found in nickname map but was reported as online."); return "§c" + match + "§r"; - }).build(), nicknames)); + }).build(), true, nicknames)); } return formatters; } diff --git a/src/main/java/buttondevteam/chat/components/formatter/formatting/ChatFormatUtils.java b/src/main/java/buttondevteam/chat/components/formatter/formatting/ChatFormatUtils.java index 00f6018..20d2083 100644 --- a/src/main/java/buttondevteam/chat/components/formatter/formatting/ChatFormatUtils.java +++ b/src/main/java/buttondevteam/chat/components/formatter/formatting/ChatFormatUtils.java @@ -4,6 +4,7 @@ import buttondevteam.chat.commands.ucmds.admin.DebugCommand; import java.util.ArrayList; import java.util.Arrays; +import java.util.stream.Collectors; public final class ChatFormatUtils { private ChatFormatUtils() {} @@ -26,6 +27,9 @@ public final class ChatFormatUtils { * Check if the given start and end position is inside any of the ranges */ static boolean isInRange(int start, int end, ArrayList ranges) { - return ranges.stream().anyMatch(range -> range[1] >= start && range[0] <= end - 1); + System.out.println("Ranges: " + ranges.stream().map(x -> x[0] + "-" + x[1]).collect(Collectors.joining(", "))); + System.out.println("In range: " + start + " " + end + ": " + + ranges.stream().filter(range -> range[1] >= start && range[0] <= end).map(x -> x[0] + "-" + x[1]).findAny().orElse("none")); + return ranges.stream().anyMatch(range -> range[1] >= start && range[0] <= end); } } diff --git a/src/main/java/buttondevteam/chat/components/formatter/formatting/ChatFormatter.java b/src/main/java/buttondevteam/chat/components/formatter/formatting/ChatFormatter.java index e3d8cbd..87e3b3a 100644 --- a/src/main/java/buttondevteam/chat/components/formatter/formatting/ChatFormatter.java +++ b/src/main/java/buttondevteam/chat/components/formatter/formatting/ChatFormatter.java @@ -10,7 +10,7 @@ import java.util.function.Predicate; import java.util.stream.Collectors; /** - * A {@link ChatFormatter} shows what formatting to use based on regular expressions. {@link ChatFormatter#Combine(List, String, TellrawPart, IHaveConfig)} is used to turn it into a {@link TellrawPart}, combining + * A {@link ChatFormatter} shows what formatting to use based on regular expressions. {@link ChatFormatter#Combine(List, String, TellrawPart, IHaveConfig, FormatSettings)}} is used to turn it into a {@link TellrawPart}, combining * intersecting parts found, for example when {@code _abc*def*ghi_} is said in chat, it'll turn it into an underlined part, then an underlined and italics part, finally an underlined part * again. * @@ -40,6 +40,8 @@ public final class ChatFormatter { */ val remchars = new ArrayList(); + escapeThings(str, excluded, remchars); + createSections(formatters, str, sections, excluded, remchars, defaults); sortSections(sections); @@ -51,18 +53,32 @@ public final class ChatFormatter { header("ChatFormatter.Combine done"); } + private static void escapeThings(String str, ArrayList ignoredAreas, ArrayList remchars) { + boolean escaped = false; + for (int i = 0; i < str.length(); i++) { + if (str.charAt(i) == '\\') { + remchars.add(new int[]{i, i}); + ignoredAreas.add(new int[]{i + 1, i + 1}); + i++; //Ignore a potential second slash + } + } + } + private static void createSections(List formatters, String str, ArrayList sections, - ArrayList excludedAreas, ArrayList removedCharacters, FormatSettings defaults) { + ArrayList excludedAreas, ArrayList removedCharacters, FormatSettings defaults) { sections.add(new FormattedSection(defaults, 0, str.length() - 1, Collections.emptyList())); //Add entire message + formatters.forEach(MatchProviderBase::reset); //Reset state information, as we aren't doing deep cloning while (formatters.size() > 0) { for (var iterator = formatters.iterator(); iterator.hasNext(); ) { MatchProviderBase formatter = iterator.next(); DebugCommand.SendDebugMessage("Checking provider: " + formatter); var sect = formatter.getNextSection(str, excludedAreas, removedCharacters); - if (sect != null) + if (sect != null) //Not excluding the area here because the range matcher shouldn't take it all sections.add(sect); - if (formatter.isFinished()) + if (formatter.isFinished()) { + DebugCommand.SendDebugMessage("Provider finished"); iterator.remove(); + } } } } @@ -103,12 +119,8 @@ public final class ChatFormatter { FormattedSection section = new FormattedSection(firstSection.Settings, lastSection.Start, origend, firstSection.Matches); section.Settings.copyFrom(lastSection.Settings); - section.Matches.addAll(lastSection.Matches); // TODO: Clean + section.Matches.addAll(lastSection.Matches); sections.add(i, section); - // Use the properties of the first section not the second one - lastSection.Settings = firstSection.Settings; - lastSection.Matches.clear(); - lastSection.Matches.addAll(firstSection.Matches); lastSection.Start = origend + 1; lastSection.End = origend2; diff --git a/src/main/java/buttondevteam/chat/components/formatter/formatting/FormatSettings.java b/src/main/java/buttondevteam/chat/components/formatter/formatting/FormatSettings.java index 12d1ee9..f4fe9c7 100644 --- a/src/main/java/buttondevteam/chat/components/formatter/formatting/FormatSettings.java +++ b/src/main/java/buttondevteam/chat/components/formatter/formatting/FormatSettings.java @@ -19,11 +19,15 @@ public class FormatSettings { public void copyFrom(FormatSettings settings) { try { - for (var field : FormatSettings.class.getDeclaredFields()) - if (field.getType() == boolean.class && field.getBoolean(settings)) - field.setBoolean(this, true); //Set to true if either of them are true - else if (field.get(this) == null) + for (var field : FormatSettings.class.getDeclaredFields()) { + if (field.getType() == boolean.class) { + if (field.getBoolean(settings)) + field.setBoolean(this, true); //Set to true if either of them are true + } else if (field.get(settings) != null) { + //System.out.println("Setting " + field.getType() + " " + field.getName() + " from " + field.get(this) + " to " + field.get(settings)); field.set(this, field.get(settings)); + } + } } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/buttondevteam/chat/components/formatter/formatting/FormattedSection.java b/src/main/java/buttondevteam/chat/components/formatter/formatting/FormattedSection.java index f6bab89..f1b0de1 100644 --- a/src/main/java/buttondevteam/chat/components/formatter/formatting/FormattedSection.java +++ b/src/main/java/buttondevteam/chat/components/formatter/formatting/FormattedSection.java @@ -15,7 +15,8 @@ public class FormattedSection { FormattedSection(FormatSettings settings, int start, int end, List matches) { Start = start; End = end; - Settings = settings; + Settings = FormatSettings.builder().build(); + Settings.copyFrom(settings); Matches.addAll(matches); } } \ No newline at end of file diff --git a/src/main/java/buttondevteam/chat/components/formatter/formatting/MatchProvider.java b/src/main/java/buttondevteam/chat/components/formatter/formatting/MatchProvider.java index f232dd8..13dafca 100644 --- a/src/main/java/buttondevteam/chat/components/formatter/formatting/MatchProvider.java +++ b/src/main/java/buttondevteam/chat/components/formatter/formatting/MatchProvider.java @@ -10,4 +10,9 @@ public interface MatchProvider { boolean isFinished(); String getName(); + + @Override + String toString(); + + void reset(); } diff --git a/src/main/java/buttondevteam/chat/components/formatter/formatting/MatchProviderBase.java b/src/main/java/buttondevteam/chat/components/formatter/formatting/MatchProviderBase.java index 548a007..8bfa824 100644 --- a/src/main/java/buttondevteam/chat/components/formatter/formatting/MatchProviderBase.java +++ b/src/main/java/buttondevteam/chat/components/formatter/formatting/MatchProviderBase.java @@ -22,6 +22,13 @@ public abstract class MatchProviderBase implements MatchProvider { @Override public abstract String toString(); + protected abstract void resetSubclass(); + + public void reset() { + finished = false; + resetSubclass(); + } + ConfigData enabled(IHaveConfig config) { return config.getData(name + ".enabled", true); } diff --git a/src/main/java/buttondevteam/chat/components/formatter/formatting/RangeMatchProvider.java b/src/main/java/buttondevteam/chat/components/formatter/formatting/RangeMatchProvider.java index c28e9d1..4df0b22 100644 --- a/src/main/java/buttondevteam/chat/components/formatter/formatting/RangeMatchProvider.java +++ b/src/main/java/buttondevteam/chat/components/formatter/formatting/RangeMatchProvider.java @@ -24,16 +24,18 @@ public class RangeMatchProvider extends MatchProviderBase { @Override public FormattedSection getNextSection(String message, ArrayList ignoredAreas, ArrayList removedCharacters) { int i, len; - do { - i = message.indexOf(pattern, nextIndex); - len = pattern.length(); - nextIndex = i + len; //Set for the next loop if it's escaped or for the next method call - } while (i > 0 && message.charAt(i - 1) == '\\'); + i = message.indexOf(pattern, nextIndex); + len = pattern.length(); + nextIndex = i + len; //Set for the next method call if (i == -1) { finished = true; //Won't find any more - unfinished sections will be garbage collected return null; } - removedCharacters.add(new int[]{i, i + len - 1}); + if (ChatFormatUtils.isInRange(i, i + len - 1, ignoredAreas)) { + DebugCommand.SendDebugMessage("Range start is in ignored area, skipping"); + return null; //Not setting finished to true, so it will go to the next match + } + ignoredAreas.add(new int[]{i, i + len - 1}); if (startedSection == null) { DebugCommand.SendDebugMessage("Started range match from " + i + " to " + (i + len - 1)); DebugCommand.SendDebugMessage("With settings: " + settings); @@ -41,11 +43,21 @@ public class RangeMatchProvider extends MatchProviderBase { startedSection = new FormattedSection(settings, i, i + len - 1, Collections.emptyList()); return null; } else { - DebugCommand.SendDebugMessage("Finished range match from " + i + " to " + (i + len - 1)); + var section = startedSection; + DebugCommand.SendDebugMessage("Finished range match from " + section.Start + " to " + (i + len - 1)); DebugCommand.SendDebugMessage("With settings: " + settings); - ChatFormatUtils.sendMessageWithPointer(message, i, i + len - 1); - startedSection.End = i + len - 1; - return startedSection; + ChatFormatUtils.sendMessageWithPointer(message, section.Start, i + len - 1); + section.End = i + len - 1; + removedCharacters.add(new int[]{section.Start, section.Start + len - 1}); + removedCharacters.add(new int[]{i, i + len - 1}); + startedSection = null; //Reset so next find creates a new one + return section; } } + + @Override + public void resetSubclass() { + nextIndex = 0; + startedSection = null; + } } diff --git a/src/main/java/buttondevteam/chat/components/formatter/formatting/RegexMatchProvider.java b/src/main/java/buttondevteam/chat/components/formatter/formatting/RegexMatchProvider.java index 477c616..bbd4b84 100644 --- a/src/main/java/buttondevteam/chat/components/formatter/formatting/RegexMatchProvider.java +++ b/src/main/java/buttondevteam/chat/components/formatter/formatting/RegexMatchProvider.java @@ -35,7 +35,7 @@ public class RegexMatchProvider extends MatchProviderBase { DebugCommand.SendDebugMessage("With settings: " + settings); ChatFormatUtils.sendMessageWithPointer(message, start, end); if (ChatFormatUtils.isInRange(start, end, ignoredAreas)) { - DebugCommand.SendDebugMessage("Formatter is in ignored area, skipping"); + DebugCommand.SendDebugMessage("Match is in ignored area, skipping"); return null; //Not setting finished to true, so it will go to the next match } ArrayList groups = new ArrayList<>(); @@ -43,6 +43,12 @@ public class RegexMatchProvider extends MatchProviderBase { groups.add(matcher.group(i + 1)); if (groups.size() > 0) DebugCommand.SendDebugMessage("First group: " + groups.get(0)); + ignoredAreas.add(new int[]{start, end}); return new FormattedSection(settings, matcher.start(), matcher.end() - 1, groups); } + + @Override + public void resetSubclass() { + matcher = null; + } } diff --git a/src/main/java/buttondevteam/chat/components/formatter/formatting/StringMatchProvider.java b/src/main/java/buttondevteam/chat/components/formatter/formatting/StringMatchProvider.java index beabce1..9ed3d8d 100644 --- a/src/main/java/buttondevteam/chat/components/formatter/formatting/StringMatchProvider.java +++ b/src/main/java/buttondevteam/chat/components/formatter/formatting/StringMatchProvider.java @@ -13,6 +13,7 @@ public class StringMatchProvider extends MatchProviderBase { @ToString.Exclude private final FormatSettings settings; private int nextIndex = 0; + private boolean ignoreCase; /** * Matches the given strings in the order given @@ -20,15 +21,23 @@ public class StringMatchProvider extends MatchProviderBase { * @param settings The format settings * @param strings The strings to match in the correct order */ - public StringMatchProvider(String name, FormatSettings settings, String... strings) { + public StringMatchProvider(String name, FormatSettings settings, boolean ignoreCase, String... strings) { super(name); this.settings = settings; this.strings = strings; + this.ignoreCase = ignoreCase; + if (ignoreCase) { + for (int i = 0; i < strings.length; i++) { + strings[i] = strings[i].toLowerCase(); + } + } } @Nullable @Override public FormattedSection getNextSection(String message, ArrayList ignoredAreas, ArrayList removedCharacters) { + if (ignoreCase) + message = message.toLowerCase(); int i = -1, len = 0; for (String string : strings) { i = message.indexOf(string, nextIndex); @@ -40,9 +49,19 @@ public class StringMatchProvider extends MatchProviderBase { return null; } nextIndex = i + len; + if (ChatFormatUtils.isInRange(i, i + len - 1, ignoredAreas)) { + DebugCommand.SendDebugMessage("String is in ignored area, skipping"); + return null; //Not setting finished to true, so it will go to the next match + } DebugCommand.SendDebugMessage("Found string match from " + i + " to " + (i + len - 1)); DebugCommand.SendDebugMessage("With settings: " + settings); ChatFormatUtils.sendMessageWithPointer(message, i, i + len - 1); + ignoredAreas.add(new int[]{i, i + len - 1}); return new FormattedSection(settings, i, i + len - 1, Collections.emptyList()); } + + @Override + public void resetSubclass() { + nextIndex = 0; + } }