From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Aikar Date: Sun, 31 May 2020 21:55:52 -0400 Subject: [PATCH] Fix serialization of colors from components This patch fixes the serialization of display names, item lores and other things which use strings with color codes. The old implementation deleted the color codes at the beginning of the resulting string if it matched the default color passed to the conversion function. This resulted in items having a black display name losing the black color code in the beginning of the text when the item was serialized (e.g. saving an ItemStack in a Yaml config). Spigot has now made the issue worse and expanded the scope to more places. Original work by (but impl pretty much fully changed): Co-Authored-By: wea_ondara diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftChatMessage.java b/src/main/java/org/bukkit/craftbukkit/util/CraftChatMessage.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/CraftChatMessage.java +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftChatMessage.java @@ -0,0 +0,0 @@ public final class CraftChatMessage { case 1: EnumChatFormat format = formatMap.get(match.toLowerCase(java.util.Locale.ENGLISH).charAt(1)); if (format == EnumChatFormat.RESET) { - modifier = new ChatModifier(); + modifier = new ChatModifier().setColor(format); // Paper } else if (format.isFormat()) { switch (format) { case BOLD: @@ -0,0 +0,0 @@ public final class CraftChatMessage { break; case 2: if (keepNewlines) { - currentChatComponent.addSibling(new ChatComponentText("\n")); + // Paper start - retain formatting for new lines + ChatComponentText ichatbasecomponent = new ChatComponentText("\n"); + ichatbasecomponent.setChatModifier(modifier.clone()); + currentChatComponent.addSibling(ichatbasecomponent); + // Paper end } else { currentChatComponent = null; } @@ -0,0 +0,0 @@ public final class CraftChatMessage { if (component == null) return ""; StringBuilder out = new StringBuilder(); + // Paper start - fix deletion of color codes at the beginning of result string + boolean hadColor = false; for (IChatBaseComponent c : (Iterable) component) { ChatModifier modi = c.getChatModifier(); - out.append(modi.getColor() == null ? defaultColor : modi.getColor()); + EnumChatFormat color = modi.getColor(); + if (!c.getText().isEmpty() || color != null) { + if (color != null) { + out.append(color); + hadColor = true; + } else if (hadColor) { + out.append(ChatColor.RESET); + hadColor = false; + } + } + // Paper end if (modi.isBold()) { out.append(EnumChatFormat.BOLD); } @@ -0,0 +0,0 @@ public final class CraftChatMessage { } out.append(c.getText()); } - return out.toString().replaceFirst("^(" + defaultColor + ")*", ""); + return out.toString(); // Paper - fix deletion of color codes at the beginning of result string } public static IChatBaseComponent fixComponent(IChatBaseComponent component) { diff --git a/src/test/java/org/bukkit/craftbukkit/CraftChatMessageTest.java b/src/test/java/org/bukkit/craftbukkit/CraftChatMessageTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 --- /dev/null +++ b/src/test/java/org/bukkit/craftbukkit/CraftChatMessageTest.java @@ -0,0 +0,0 @@ +package org.bukkit.craftbukkit; + +import net.minecraft.server.IChatBaseComponent; +import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.support.AbstractTestingBase; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class CraftChatMessageTest extends AbstractTestingBase { + @Test + public void testSimpleStrings() { + testString("§fFoo"); + testString("Foo"); + testString("Foo§bBar"); + testString("Foo§bBar"); + testString("§fFoo§bBar"); + testString("§fFoo§bBar§rBaz"); + testString("§rFoo"); + testString("Test§0\n§0\n§5Test"); + } + + @Test + public void testComponents() { + testComponent("Foo§bBar§rBaz", create("Foo", "§bBar", "Baz")); + testComponent("§fFoo§bBar§rBaz", create("", "§fFoo", "§bBar", "Baz")); + testComponent("§fFoo§bBar§rBaz", create("", "§fFoo", "§bBar", "", "Baz")); + testComponent("§fFoo§bBar§rBaz", create("§fFoo", "§bBar", "Baz")); + testComponent("Foo§bBar§rBaz", create("", "Foo", "§bBar", "Baz")); + testComponent("§fFoo§bBar§rBaz", create("§fFoo", "§bBar", "Baz")); + testComponent("F§foo§bBar§rBaz", create("F§foo", "§bBar", "Baz")); + } + + private IChatBaseComponent create(String txt, String ...rest) { + IChatBaseComponent cmp = toComp(txt); + for (String s : rest) { + cmp.addSibling(toComp(s)); + } + + return cmp; + } + + private IChatBaseComponent toComp(String txt) { + return CraftChatMessage.fromString(txt, true)[0]; + } + + private void testString(String expected) { + IChatBaseComponent cmp = toComp(expected); + String actual = CraftChatMessage.fromComponent(cmp); + assertEquals(expected, actual); + } + + private void testComponent(String expected, IChatBaseComponent components) { + String actual = CraftChatMessage.fromComponent(components); + assertEquals(expected, actual); + + IChatBaseComponent expectedCmp = toComp(expected); + String actualExpectedCmp = CraftChatMessage.fromComponent(expectedCmp); + assertEquals(expected, actualExpectedCmp); + } +}