diff --git a/common/src/main/java/com/viaversion/viabackwards/protocol/protocol1_12_2to1_13/packets/PlayerPacket1_13.java b/common/src/main/java/com/viaversion/viabackwards/protocol/protocol1_12_2to1_13/packets/PlayerPacket1_13.java index e124d1f6..09b92f55 100644 --- a/common/src/main/java/com/viaversion/viabackwards/protocol/protocol1_12_2to1_13/packets/PlayerPacket1_13.java +++ b/common/src/main/java/com/viaversion/viabackwards/protocol/protocol1_12_2to1_13/packets/PlayerPacket1_13.java @@ -203,11 +203,7 @@ public class PlayerPacket1_13 extends RewriterBase { if (mode == 0 || mode == 2) { JsonElement value = wrapper.read(Type.COMPONENT); String legacyValue = protocol.jsonToLegacy(value); - if (legacyValue.length() > 32) { - legacyValue = legacyValue.substring(0, 32); - } - - wrapper.write(Type.STRING, legacyValue); + wrapper.write(Type.STRING, ChatUtil.fromLegacy(legacyValue, 'f', 32)); int type = wrapper.read(Type.VAR_INT); wrapper.write(Type.STRING, type == 1 ? "hearts" : "integer"); } @@ -225,11 +221,7 @@ public class PlayerPacket1_13 extends RewriterBase { if (action == 0 || action == 2) { JsonElement displayName = wrapper.read(Type.COMPONENT); String legacyTextDisplayName = protocol.jsonToLegacy(displayName); - legacyTextDisplayName = ChatUtil.removeUnusedColor(legacyTextDisplayName, 'f'); - if (legacyTextDisplayName.length() > 32) { - legacyTextDisplayName = legacyTextDisplayName.substring(0, 32); - } - wrapper.write(Type.STRING, legacyTextDisplayName); + wrapper.write(Type.STRING, ChatUtil.fromLegacy(legacyTextDisplayName, 'f', 32)); byte flags = wrapper.read(Type.BYTE); String nameTagVisibility = wrapper.read(Type.STRING); @@ -247,18 +239,10 @@ public class PlayerPacket1_13 extends RewriterBase { if (ViaBackwards.getConfig().addTeamColorTo1_13Prefix()) { prefix += "§" + (colour > -1 && colour <= 15 ? Integer.toHexString(colour) : "r"); } - - prefix = ChatUtil.removeUnusedColor(prefix, 'f', true); - if (prefix.length() > 16) prefix = prefix.substring(0, 16); - if (prefix.endsWith("§")) prefix = prefix.substring(0, prefix.length() - 1); - String suffix = protocol.jsonToLegacy(suffixComponent); - suffix = ChatUtil.removeUnusedColor(suffix, '\0'); // Don't remove white coloring - if (suffix.length() > 16) suffix = suffix.substring(0, 16); - if (suffix.endsWith("§")) suffix = suffix.substring(0, suffix.length() - 1); - wrapper.write(Type.STRING, prefix); - wrapper.write(Type.STRING, suffix); + wrapper.write(Type.STRING, ChatUtil.fromLegacyPrefix(prefix, 'f', 16)); + wrapper.write(Type.STRING, ChatUtil.fromLegacy(suffix, '\0', 16)); wrapper.write(Type.BYTE, flags); wrapper.write(Type.STRING, nameTagVisibility); diff --git a/common/src/main/java/com/viaversion/viabackwards/utils/ChatUtil.java b/common/src/main/java/com/viaversion/viabackwards/utils/ChatUtil.java index 3b819dfd..57df9063 100644 --- a/common/src/main/java/com/viaversion/viabackwards/utils/ChatUtil.java +++ b/common/src/main/java/com/viaversion/viabackwards/utils/ChatUtil.java @@ -17,6 +17,9 @@ */ package com.viaversion.viabackwards.utils; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; import java.util.regex.Pattern; public class ChatUtil { @@ -27,25 +30,103 @@ public class ChatUtil { return removeUnusedColor(legacy, defaultColor, false); } + private static class ChatFormattingState { + private final Set formatting; + private final char defaultColor; + private char color; + + private ChatFormattingState(char defaultColor) { + this(new HashSet<>(), defaultColor, defaultColor); + } + + public ChatFormattingState(Set formatting, char defaultColor, char color) { + this.formatting = formatting; + this.defaultColor = defaultColor; + this.color = color; + } + + private void setColor(char newColor) { + formatting.clear(); + color = newColor; + } + + public ChatFormattingState copy() { + return new ChatFormattingState(new HashSet<>(formatting), defaultColor, color); + } + + public void appendTo(StringBuilder builder) { + builder.append('§').append(color); + for (Character formatCharacter : formatting) { + builder.append('§').append(formatCharacter); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ChatFormattingState that = (ChatFormattingState) o; + return defaultColor == that.defaultColor + && color == that.color + && Objects.equals(formatting, that.formatting); + } + + @Override + public int hashCode() { + return Objects.hash(formatting, defaultColor, color); + } + + public void processNextControlChar(char controlChar) { + if (controlChar == 'r') { + setColor(defaultColor); + return; + } + if (controlChar == 'l' || controlChar == 'm' || controlChar == 'n' || controlChar == 'o') { + formatting.add(controlChar); + return; + } + setColor(controlChar); + } + } + + public static String fromLegacy(String legacy, char defaultColor, int limit) { + return fromLegacy(legacy, defaultColor, limit, false); + } + + public static String fromLegacyPrefix(String legacy, char defaultColor, int limit) { + return fromLegacy(legacy, defaultColor, limit, true); + } + + public static String fromLegacy(String legacy, char defaultColor, int limit, boolean isPrefix) { + legacy = removeUnusedColor(legacy, defaultColor, isPrefix); + if (legacy.length() > limit) legacy = legacy.substring(0, limit); + if (legacy.endsWith("§")) legacy = legacy.substring(0, legacy.length() - 1); + return legacy; + } + public static String removeUnusedColor(String legacy, char defaultColor, boolean isPrefix) { if (legacy == null) return null; - Pattern pattern = isPrefix ? UNUSED_COLOR_PATTERN_PREFIX : UNUSED_COLOR_PATTERN; legacy = pattern.matcher(legacy).replaceAll("$1$2"); StringBuilder builder = new StringBuilder(); - char last = defaultColor; + ChatFormattingState builderState = new ChatFormattingState(defaultColor); + ChatFormattingState lastState = new ChatFormattingState(defaultColor); for (int i = 0; i < legacy.length(); i++) { char current = legacy.charAt(i); if (current != '§' || i == legacy.length() - 1) { + if (!lastState.equals(builderState)) { + lastState.appendTo(builder); + builderState = lastState.copy(); + } builder.append(current); continue; } - current = legacy.charAt(++i); - if (current != last) { - builder.append('§').append(current); - last = current; - } + lastState.processNextControlChar(current); } return builder.toString(); }