Paper/Spigot-Server-Patches/0534-Fix-serialization-of-colors-from-components.patch
Aikar d7cfa4fa5c
Improve legacy format serialization more
This should now complete legacy serialization to avoid ever
changing the output content.

This removes the concept of "Default Color" from the method as
that entire concept was flawed and broke the intent of chat components.

Going to actually PR this patch to Spigot soon.

This now puts us back at a point where any data saved pre Spigot
breaking things will still save back the exact same way as before,

but new component -> legacy will now be fixed to not insert undesirable
default colors (such as black) into the legacy string, and instead use
the proper reset code.

This means you can now safety get the text from a book and
put it in chat or an entity display name without worry about black
color codes or other undesired color codes leaking into the new
context where that color doesn't make sense.
2020-05-31 22:13:17 -04:00

144 lines
6.3 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
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 <wea_ondara@alpenblock.net>
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftChatMessage.java b/src/main/java/org/bukkit/craftbukkit/util/CraftChatMessage.java
index 2e162b9ea31c8bf81cfa5282566b37fc29537f51..7edb88d515e3dab0d81b8f7a545ab614b4b51489 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftChatMessage.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftChatMessage.java
@@ -67,7 +67,7 @@ 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:
@@ -94,7 +94,11 @@ 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;
}
@@ -172,9 +176,21 @@ 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<IChatBaseComponent>) 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);
}
@@ -192,7 +208,7 @@ 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..759049174d2113837aa06faea82ddcc683b4d9c6
--- /dev/null
+++ b/src/test/java/org/bukkit/craftbukkit/CraftChatMessageTest.java
@@ -0,0 +1,61 @@
+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);
+ }
+}