diff --git a/changelog.md b/changelog.md
index 7d15fa3..85411f7 100644
--- a/changelog.md
+++ b/changelog.md
@@ -18,6 +18,7 @@
- Refactor StringTextUtils to TextUtils
- Restructured the powerhouse `ChatHudMixin#modifyMessage(Text, boolean)` method to be more modular with message reconstruction
- Moved the bulk of the `modifyMessage` method to ChatUtils to help development and greatly ease future troubleshooting
+ - Created a new `ChatUtils#getArg(..)` method to avoid the elusive `ClassCastException`s that kept getting thrown
- Tweaked the `MessageHandlerMixin#cacheGameData` method to use built-in methods instead of rewriting the same thing
- Removed the `VANILLA_MESSAGE` matcher in `ChatUtils` because it was redundant
diff --git a/src/main/java/obro1961/chatpatches/ChatPatches.java b/src/main/java/obro1961/chatpatches/ChatPatches.java
index 79f9aff..8aceab0 100644
--- a/src/main/java/obro1961/chatpatches/ChatPatches.java
+++ b/src/main/java/obro1961/chatpatches/ChatPatches.java
@@ -83,6 +83,25 @@ public void onInitializeClient() {
}
+ /**
+ * Logs an error-level message telling the user to report
+ * the given error. The class and method of the caller is
+ * provided from a {@link StackWalker}.
+ *
+ * Outputs the following message:
+ *
+ * [$class.$method] /!\ Please report this error on GitHub or Discord with the full log file attached! /!\
+ * (error)
+ *
+ */
+ public static void logInfoReportMessage(Throwable error) {
+ StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
+ String clazz = walker.getCallerClass().getSimpleName();
+ String method = walker.walk(frames -> frames.skip(1).findFirst().orElseThrow().getMethodName());
+ method = method.isBlank() ? error.getStackTrace()[0].getMethodName() : method;
+ LOGGER.error("[%s.%s] /!\\ Please report this error on GitHub or Discord with the full log file attached! /!\\".formatted(clazz, method), error);
+ }
+
/**
* Creates a new Identifier using the ChatPatches mod ID.
*/
diff --git a/src/main/java/obro1961/chatpatches/config/Config.java b/src/main/java/obro1961/chatpatches/config/Config.java
index 577278d..4c4df1e 100644
--- a/src/main/java/obro1961/chatpatches/config/Config.java
+++ b/src/main/java/obro1961/chatpatches/config/Config.java
@@ -119,28 +119,38 @@ public Style makeHoverStyle(Date when) {
* @implNote {@code player} must reference a valid, existing
* player entity and have both a valid name and UUID.
*/
- public MutableText formatPlayername(GameProfile player) {
- // todo add some error handling here
- PlayerEntity entity = MinecraftClient.getInstance().world.getPlayerByUuid(player.getId());
- Team team = entity.getScoreboard().getPlayerTeam(player.getName());
- Style style = entity.getDisplayName().getStyle().withColor( entity.getTeamColorValue() != 0xffffff ? entity.getTeamColorValue() : chatNameColor );
-
- if(team != null) {
- // note: doesn't set the style on every append, as it's already set in the parent text. might cause issues?
- // if the player is on a team, add the prefix and suffixes from the config AND team (if they exist) to the formatted name
- MutableText playername = text(player.getName());
- Text configPrefix = text(chatNameFormat.split("\\$")[0]);
- Text configSuffix = text(chatNameFormat.split("\\$")[1] + " ");
-
- return Text.empty().setStyle(style)
- .append(configPrefix)
- .append(team.getPrefix())
- .append(playername)
- .append(team.getSuffix())
- .append(configSuffix);
- } else {
- return makeObject(chatNameFormat, player.getName(), "", " ", style);
+ public MutableText formatPlayername(GameProfile profile) {
+ Style style = BLANK_STYLE.withColor(chatNameColor);
+ try {
+ PlayerEntity entity = MinecraftClient.getInstance().world.getPlayerByUuid(profile.getId());
+ Team team = null;
+
+ if(entity != null) {
+ team = entity.getScoreboard().getPlayerTeam(profile.getName());
+ style = entity.getDisplayName().getStyle().withColor( entity.getTeamColorValue() != 0xffffff ? entity.getTeamColorValue() : chatNameColor );
+ }
+
+ if(team != null) {
+ // note: doesn't set the style on every append, as it's already set in the parent text. might cause issues?
+ // if the player is on a team, add the prefix and suffixes from the config AND team (if they exist) to the formatted name
+ MutableText playername = text(profile.getName());
+ String[] configFormat = chatNameFormat.split("\\$");
+ Text configPrefix = text(configFormat[0]);
+ Text configSuffix = text(configFormat[1] + " ");
+
+ return Text.empty().setStyle(style)
+ .append(configPrefix)
+ .append(team.getPrefix())
+ .append(playername)
+ .append(team.getSuffix())
+ .append(configSuffix);
+ }
+ } catch(Exception e) {
+ LOGGER.error("[Config.formatPlayername] /!\\ An error occurred while trying to format '{}'s playername /!\\", profile.getName());
+ ChatPatches.logInfoReportMessage(e);
}
+
+ return makeObject(chatNameFormat, profile.getName(), "", " ", style);
}
public MutableText makeDupeCounter(int dupes) {
@@ -236,7 +246,9 @@ public static ConfigOption getOption(String key) {
try {
return new ConfigOption<>( (T)config.getClass().getField(key).get(config), (T)config.getClass().getField(key).get(DEFAULTS), key );
} catch(IllegalAccessException | NoSuchFieldException e) {
- LOGGER.error("[Config.getOption({})] An error occurred while trying to get an option value, please report this on GitHub:", key, e);
+ LOGGER.error("[Config.getOption({})] An error occurred while trying to get an option value!", key);
+ ChatPatches.logInfoReportMessage(e);
+
return new ConfigOption<>( (T)new Object(), (T)new Object(), key );
}
}
diff --git a/src/main/java/obro1961/chatpatches/mixin/gui/ChatHudMixin.java b/src/main/java/obro1961/chatpatches/mixin/gui/ChatHudMixin.java
index 6cb13c2..9306fa0 100644
--- a/src/main/java/obro1961/chatpatches/mixin/gui/ChatHudMixin.java
+++ b/src/main/java/obro1961/chatpatches/mixin/gui/ChatHudMixin.java
@@ -11,11 +11,7 @@
import net.minecraft.client.gui.hud.ChatHudLine;
import net.minecraft.client.gui.hud.MessageIndicator;
import net.minecraft.client.util.CommandHistoryManager;
-import net.minecraft.text.MutableText;
-import net.minecraft.text.Style;
import net.minecraft.text.Text;
-import net.minecraft.text.TranslatableTextContent;
-import net.minecraft.util.Util;
import net.minecraft.util.math.MathHelper;
import obro1961.chatpatches.ChatPatches;
import obro1961.chatpatches.accessor.ChatHudAccessor;
@@ -33,12 +29,9 @@
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-import java.util.ArrayList;
-import java.util.Date;
import java.util.List;
import static obro1961.chatpatches.ChatPatches.config;
-import static obro1961.chatpatches.ChatPatches.msgData;
import static obro1961.chatpatches.util.ChatUtils.MESSAGE_INDEX;
import static obro1961.chatpatches.util.ChatUtils.getPart;
@@ -134,16 +127,10 @@ private double moveINDHoverText(double e) {
/**
* Modifies the incoming message by adding timestamps, nicer
* player names, hover events, and duplicate counters in conjunction with
- * {@link #addCounter(Text, boolean)}
- *
- * @implNote
- * Doesn't modify when {@code refreshing} is true, as that signifies
- * re-rendering of chat messages on the hud.
- * This method causes all messages passed to it to be formatted in
- * a new structure for clear data access. This is mostly done using
- * {@link MutableText#append(Text)}, which deliberately puts message
- * components at specific indices, all of which are laid out in
- * {@link ChatUtils}.
+ * {@link #addCounter(Text, boolean)}.
+ *
+ * See {@link ChatUtils#modifyMessage(Text, boolean)} for detailed
+ * implementation specifications.
*/
@ModifyVariable(
method = "addMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/message/MessageSignatureData;ILnet/minecraft/client/gui/hud/MessageIndicator;Z)V",
@@ -151,86 +138,7 @@ private double moveINDHoverText(double e) {
argsOnly = true
)
private Text modifyMessage(Text m, @Local(argsOnly = true) boolean refreshing) {
- if( refreshing || Flags.LOADING_CHATLOG.isRaised() )
- return addCounter(m, refreshing); // cancels modifications when loading the chatlog or regenerating visibles
-
- Style style = m.getStyle();
- boolean lastEmpty = msgData.equals(ChatUtils.NIL_MSG_DATA);
- boolean boundary = Flags.BOUNDARY_LINE.isRaised() && config.boundary && !config.vanillaClearing;
- Date now = lastEmpty ? new Date() : msgData.timestamp();
- String nowStr = String.valueOf( now.getTime() ); // for copy menu and storing timestamp data! only affects the timestamp
-
-//ChatPatches.LOGGER.warn("received {} message: '{}'", m.getContent().getClass().getSimpleName(), m.getString());
-
- MutableText timestamp = (config.time && !boundary) ? config.makeTimestamp(now).setStyle( config.makeHoverStyle(now) ) : Text.empty().styled(s -> s.withInsertion(nowStr));
- MutableText content = Text.empty().setStyle(style);
-
- // reconstruct the player message if it's in the vanilla format and it should be reformatted
- if(!lastEmpty && !boundary && msgData.vanilla()) {
- // if the message is translatable, then we know exactly where everything is
- if(m.getContent() instanceof TranslatableTextContent ttc && ttc.getKey().matches("chat.type.(text|team.(text|sent))")) {
- String key = ttc.getKey();
-
- // adds the team name for team messages
- if(key.startsWith("chat.type.team.")) {
- MutableText teamPart = Text.empty();
- // adds the preceding arrow for sent team messages
- if(key.endsWith("sent"))
- teamPart.append(Text.literal("-> ").setStyle(style));
-
- // adds the team name for team messages
- teamPart.append( ((MutableText)ttc.getArg(0)).append(" ") );
-
- content.append(teamPart);
- } else {
- content.append(""); // if there isn't a team message, add an empty string to keep the index constant
- }
-
- // adds the formatted playername and content for all message types
- content.append(config.formatPlayername(msgData.sender())); // sender data is already known
- content.append((Text) ttc.getArg(ttc.getArgs().length - 1)); // always at the end
- } else { // reconstructs the message if it matches the vanilla format '<%s> %s' but isn't translatable
- // collect all message parts into one list, including the root TextContent
- // (assuming this accounts for all parts, TextContents, and siblings)
- List parts = Util.make(new ArrayList<>(m.getSiblings().size() + 1), a -> {
- if(!m.equals(Text.EMPTY))
- a.add( m.copyContentOnly().setStyle(style) );
-
- a.addAll( m.getSiblings() );
- });
-
- MutableText realContent = Text.empty();
- // find the index of the end of a '<%s> %s' message
- Text firstPart = parts.stream().filter(p -> p.getString().contains(">")).findFirst()
- .orElseThrow(() -> new IllegalStateException("No closing angle bracket found in vanilla message '" + m.getString() + "' !"));
- String afterEndBracket = firstPart.getString().split(">")[1]; // just get the part after the closing bracket, we know the start
-
- // adds the part after the closing bracket but before any remaining siblings, if it exists
- if(!afterEndBracket.isEmpty())
- realContent.append( Text.literal(afterEndBracket).setStyle(firstPart.getStyle()) );
-
- // we know everything remaining is message content parts, so add everything
- for(int i = parts.indexOf(firstPart) + 1; i < parts.size(); i++)
- realContent.append(parts.get(i));
-
- content.append(config.formatPlayername(msgData.sender())); // sender data is already known
- content.append(realContent); // adds the reconstructed message content
- }
-//ChatPatches.LOGGER.warn("DID!!! reformat, content: '{}' aka '{}'+{}", content.getString(), content.copyContentOnly().getString(), content.getSiblings());//delete:-
- } else {
- // don't reformat if it isn't vanilla or needed
- content = m.copy();
-//ChatPatches.LOGGER.warn("didn't reformat, content: '{}' aka '{}'+{}", content.getString(), content.copyContentOnly().getString(), content.getSiblings());//delete:-
- }
-
- // assembles constructed message and adds a duplicate counter according to the #addCounter method
- Text modified = addCounter( ChatUtils.buildMessage(style, timestamp, content, null), false );
-/*ChatPatches.LOGGER.info("------ parts of message ------\n\ttimestamp: '{}'\n\tmessage: ('{}')\n\t\tteam: '{}'\n\t\tsender: '{}'\n\t\tcontent: '{}'\n\tdupe: '{}'\n------",
-ChatUtils.getPart(modified, 0), ChatUtils.getPart(modified, 1), ChatUtils.getMsgPart(modified, 0), ChatUtils.getMsgPart(modified, 1), ChatUtils.getMsgPart(modified, 2), ChatUtils.getPart(modified, 2)
-);*/ //delete:-
- ChatLog.addMessage(modified);
- msgData = ChatUtils.NIL_MSG_DATA; // fixes messages that get around MessageHandlerMixin's data caching, usually thru ChatHud#addMessage (ex. open-to-lan message)
- return modified;
+ return addCounter(ChatUtils.modifyMessage(m, refreshing), refreshing);
}
@Inject(method = "addToMessageHistory", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/collection/ArrayListDeque;size()I"))
@@ -311,10 +219,11 @@ private Text addCounter(Text incoming, boolean refreshing) {
}
} catch(IndexOutOfBoundsException e) {
ChatPatches.LOGGER.error("[ChatHudMixin.addCounter] Couldn't add duplicate counter because message '{}' ({} parts) was not constructed properly.", incoming.getString(), incoming.getSiblings().size());
- ChatPatches.LOGGER.error("[ChatHudMixin.addCounter] This could have also been caused by an issue with the new CompactChat dupe-condensing method.");
- ChatPatches.LOGGER.error("[ChatHudMixin.addCounter] Either way, this was caused by a bug or mod incompatibility. Please report this on GitHub or on the Discord!", e);
+ ChatPatches.LOGGER.error("[ChatHudMixin.addCounter] This could have also been caused by an issue with the new CompactChat dupe-condensing method. Either way,");
+ ChatPatches.logInfoReportMessage(e);
} catch(Exception e) {
- ChatPatches.LOGGER.error("[ChatHudMixin.addCounter] /!\\ Couldn't add duplicate counter because of an unexpected error. Please report this on GitHub or on the Discord! /!\\", e);
+ ChatPatches.LOGGER.error("[ChatHudMixin.addCounter] /!\\ Couldn't add duplicate counter because of an unexpected error! /!\\");
+ ChatPatches.logInfoReportMessage(e);
}
return incoming;
diff --git a/src/main/java/obro1961/chatpatches/util/ChatUtils.java b/src/main/java/obro1961/chatpatches/util/ChatUtils.java
index 3d1865c..6bf1226 100644
--- a/src/main/java/obro1961/chatpatches/util/ChatUtils.java
+++ b/src/main/java/obro1961/chatpatches/util/ChatUtils.java
@@ -5,21 +5,20 @@
import net.minecraft.client.gui.hud.ChatHud;
import net.minecraft.client.gui.hud.ChatHudLine;
import net.minecraft.client.util.ChatMessages;
-import net.minecraft.text.MutableText;
-import net.minecraft.text.OrderedText;
-import net.minecraft.text.Style;
-import net.minecraft.text.Text;
+import net.minecraft.text.*;
+import net.minecraft.util.Util;
import net.minecraft.util.math.MathHelper;
+import obro1961.chatpatches.ChatPatches;
import obro1961.chatpatches.accessor.ChatHudAccessor;
+import obro1961.chatpatches.chatlog.ChatLog;
import obro1961.chatpatches.mixin.gui.ChatHudMixin;
+import org.jetbrains.annotations.NotNull;
import java.time.Instant;
-import java.util.Date;
-import java.util.List;
-import java.util.Objects;
-import java.util.UUID;
+import java.util.*;
import static obro1961.chatpatches.ChatPatches.config;
+import static obro1961.chatpatches.ChatPatches.msgData;
import static obro1961.chatpatches.util.TextUtils.copyWithoutContent;
import static obro1961.chatpatches.util.TextUtils.reorder;
@@ -56,6 +55,40 @@ public static Text getMsgPart(Text message, int index) {
return getPart(getPart(message, MESSAGE_INDEX), index);
}
+ /**
+ * Returns a MutableText object representing the argument
+ * located at the given index of the given
+ * {@link TranslatableTextContent}. Needed because of a weird
+ * phenomenon where the {@link TranslatableTextContent#getArg(int)}
+ * method can return a {@link String} or other non-Text related
+ * object, which otherwise causes {@link ClassCastException}s.
+ *
+ * Wraps {@link String}s in {@link Text#literal(String)}
+ * and nulls in {@link Text#empty()}.
+ *
+ * @implNote
+ * If {@code index} is negative, adds it to the args array
+ * length. In other words, passing index {@code -n} will
+ * get the {@code content.getArgs().length-n}th argument.
+ */
+ public static MutableText getArg(TranslatableTextContent content, int index) {
+ if(index < 0)
+ index = content.getArgs().length + index;
+
+ Object /* StringVisitable */ arg = content.getArg(index);
+
+ if(arg == null)
+ return Text.empty();
+ else if(arg instanceof Text t)
+ return (MutableText) t;
+ else if(arg instanceof StringVisitable sv)
+ return Text.literal(sv.getString());
+ else if(arg instanceof String s)
+ return Text.literal(s);
+ else
+ return Text.empty();
+ }
+
/**
* Builds a chat message from the given components.
* If anything is {@code null}, it is replaced with
@@ -85,19 +118,142 @@ public static MutableText buildMessage(Style rootStyle, Text first, Text second,
}
/**
- * todo doc AFTER todo moving impl here
+ * Reformats the incoming message {@code m} according to configured
+ * settings, message data, and at indices specified in this class.
+ * This method is used in the {@link ChatHudMixin#modifyMessage(Text, boolean)}
+ * mixin for functionality.
+ *
+ * @implNote
+ *
+ * - Don't modify when {@code refreshing} is true, as that signifies
+ * re-rendering chat messages, so simply return {@code m}.
+ * - Declare relevant variables, most notably the {@code timestamp}
+ * and {@code content} components.
+ * - Reconstruct the player message if it should be reformatted
+ * (message has player data, not a boundary line, and in vanilla
+ * format):
+ *
+ * - If the message is translatable and in a known format:
+ *
+ * - If the message is a team message, add all related
+ * team message components.
+ * - Add the formatted playername and content.
+ *
+ *
+ * - Otherwise, the message must be in an unknown format where all
+ * we know for sure is the format ({@code <$name> $message}):
+ *
+ * - Collect all message components into a list, including the
+ * root {@link TextContent} (assuming this accounts for all parts,
+ * {@link TextContent}s, and siblings).
+ * - Find the first part that contains a '>'.
+ * - Add the part after the '>' but before any
+ * remaining siblings, if it exists, to the {@code realContent}
+ * local Text variable (with the proper Style).
+ * - Add every part succeeding the '>' component to
+ * the {@code realContent} variable.
+ * - Add the formatted playername and {@code realContent}
+ * variable to the actual content.
+ *
+ *
+ *
+ *
+ * - If the message shouldn't be formatted (doesn't satisfy all
+ * prerequisites), then don't change {@code m} and store it.
+ * - Assemble the constructed message and add a duplicate counter
+ * according to the {@link ChatHudMixin#addCounter(Text, boolean)} method.
+ * - Log the modified message in the {@code ChatLog}.
+ * - Reset the {@link ChatPatches#msgData} to prevent an uncommon bug.
+ * - Return the modified message, regardless of if it was
+ *
*/
- public static Text modifyMessage(Text message, boolean vanilla) {
- // early if-return checks
+ public static Text modifyMessage(@NotNull Text m, boolean refreshing) {
+ if( refreshing || Flags.LOADING_CHATLOG.isRaised() )
+ return m; // cancels modifications when loading the chatlog or regenerating visibles
+
+ boolean lastEmpty = msgData.equals(ChatUtils.NIL_MSG_DATA);
+ boolean boundary = Flags.BOUNDARY_LINE.isRaised() && config.boundary && !config.vanillaClearing;
+ Date now = lastEmpty ? new Date() : msgData.timestamp();
+ String nowStr = String.valueOf(now.getTime()); // for copy menu and storing timestamp data! only affects the timestamp
+ Style style = m.getStyle();
- // assign variables
+ MutableText timestamp = null;
+ MutableText content = m.copy();
- // declare message parts
- // if TranslatableTextContent and known keys, store pre-formatted parts instantly
- // else do typical formatting stuff (except optimize it to make it actually work and not ugly)
+ try {
+ timestamp = (config.time && !boundary) ? config.makeTimestamp(now).setStyle( config.makeHoverStyle(now) ) : Text.empty().styled(s -> s.withInsertion(nowStr));
+ content = Text.empty().setStyle(style);
+
+ // reconstruct the player message if it's in the vanilla format and it should be reformatted
+ if(!lastEmpty && !boundary && msgData.vanilla()) {
+ // if the message is translatable, then we know exactly where everything is
+ if(m.getContent() instanceof TranslatableTextContent ttc && ttc.getKey().matches("chat.type.(text|team.(text|sent))")) {
+ String key = ttc.getKey();
+
+ // adds the team name for team messages
+ if(key.startsWith("chat.type.team.")) {
+ MutableText teamPart = Text.empty();
+ // adds the preceding arrow for sent team messages
+ if(key.endsWith("sent"))
+ teamPart.append(Text.literal("-> ").setStyle(style));
+
+ // adds the team name for team messages
+ teamPart.append(getArg(ttc, 0).append(" "));
+
+ content.append(teamPart);
+ } else {
+ content.append(""); // if there isn't a team message, add an empty string to keep the index constant
+ }
+
+ // adds the formatted playername and content for all message types
+ content.append(config.formatPlayername(msgData.sender())); // sender data is already known
+ content.append(getArg(ttc, -1)); // always at the end
+ } else { // reconstructs the message if it matches the vanilla format '<%s> %s' but isn't translatable
+ // collect all message parts into one list, including the root TextContent
+ // (assuming this accounts for all parts, TextContents, and siblings)
+ List parts = Util.make(new ArrayList<>(m.getSiblings().size() + 1), a -> {
+ if(!m.equals(Text.EMPTY))
+ a.add( m.copyContentOnly().setStyle(style) );
+
+ a.addAll( m.getSiblings() );
+ });
+
+ MutableText realContent = Text.empty();
+ // find the first index of a '>' in the message, is formatted like '<%s> %s'
+ Text firstPart = parts.stream().filter(p -> p.getString().contains(">")).findFirst()
+ .orElseThrow(() -> new IllegalStateException("No closing angle bracket found in vanilla message '" + m.getString() + "' !"));
+ String afterEndBracket = firstPart.getString().split(">")[1]; // just get the part after the closing bracket, we know the start
+
+ // ignore everything before the '>' because it's the playername, which we already know
+ // adds the part after the closing bracket but before any remaining siblings, if it exists
+ if(!afterEndBracket.isEmpty())
+ realContent.append( Text.literal(afterEndBracket).setStyle(firstPart.getStyle()) );
+
+ // we know everything remaining is message content parts, so add everything
+ for(int i = parts.indexOf(firstPart) + 1; i < parts.size(); i++)
+ realContent.append(parts.get(i));
+
+ content.append(config.formatPlayername(msgData.sender())); // sender data is already known
+ content.append(realContent); // adds the reconstructed message content
+ }
+ } else {
+ // don't reformat if it isn't vanilla or needed
+ content = m.copy();
+ }
+ } catch(Throwable e) {
+ ChatPatches.LOGGER.error("[ChatUtils.modifyMessage] An error occurred while modifying message '{}', returning original:", m.getString());
+ ChatPatches.LOGGER.debug("[ChatUtils.modifyMessage] \tOriginal message structure: {}", m);
+ ChatPatches.LOGGER.debug("[ChatUtils.modifyMessage] \tModified message structure:");
+ ChatPatches.LOGGER.debug("[ChatUtils.modifyMessage] \t\tTimestamp structure: {}", timestamp);
+ ChatPatches.LOGGER.debug("[ChatUtils.modifyMessage] \t\tContent structure: {}", content);
+ ChatPatches.logInfoReportMessage(e);
+ }
- // final cleanup and logging
- return message;
+ // assembles constructed message and adds a duplicate counter according to the #addCounter method
+ Text modified = ChatUtils.buildMessage(style, timestamp, content, null);
+ ChatLog.addMessage(modified);
+ msgData = ChatUtils.NIL_MSG_DATA; // fixes messages that get around MessageHandlerMixin's data caching, usually thru ChatHud#addMessage (ex. open-to-lan message)
+ return modified;
}
/**