mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Improvements to MessageTranslator (#3803)
* Renames for clarity and refactor convertToJavaMessage * Bump adventure, velociy. Require CharacterAndFormat in MessageTranslator * Fix deprecations related to DummyLegacyHoverEventSerializer * Patch serialization of ScoreComponent until Adventure 1.15.0
This commit is contained in:
parent
706d1b9627
commit
661a9b4741
7 changed files with 63 additions and 65 deletions
|
@ -75,7 +75,7 @@ public class AnvilContainer extends Container {
|
|||
|
||||
String originalName = ItemUtils.getCustomName(getInput().getNbt());
|
||||
|
||||
String plainOriginalName = MessageTranslator.convertToPlainText(originalName, session.locale());
|
||||
String plainOriginalName = MessageTranslator.convertToPlainTextLenient(originalName, session.locale());
|
||||
String plainNewName = MessageTranslator.convertToPlainText(rename);
|
||||
if (!plainOriginalName.equals(plainNewName)) {
|
||||
// Strip out formatting since Java Edition does not allow it
|
||||
|
|
|
@ -118,7 +118,7 @@ public class AnvilInventoryUpdater extends InventoryUpdater {
|
|||
|
||||
// Changing the item in the input slot resets the name field on Bedrock, but
|
||||
// does not result in a FilterTextPacket
|
||||
String originalName = MessageTranslator.convertToPlainText(ItemUtils.getCustomName(input.getNbt()), session.locale());
|
||||
String originalName = MessageTranslator.convertToPlainTextLenient(ItemUtils.getCustomName(input.getNbt()), session.locale());
|
||||
ServerboundRenameItemPacket renameItemPacket = new ServerboundRenameItemPacket(originalName);
|
||||
session.sendDownstreamPacket(renameItemPacket);
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ package org.geysermc.geyser.text;
|
|||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.serializer.gson.LegacyHoverEventSerializer;
|
||||
import net.kyori.adventure.text.serializer.json.LegacyHoverEventSerializer;
|
||||
import net.kyori.adventure.util.Codec;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
@ -40,9 +40,9 @@ public final class DummyLegacyHoverEventSerializer implements LegacyHoverEventSe
|
|||
private final HoverEvent.ShowItem dummyShowItem;
|
||||
|
||||
public DummyLegacyHoverEventSerializer() {
|
||||
dummyShowEntity = HoverEvent.ShowEntity.of(Key.key("geysermc", "dummyshowitem"),
|
||||
dummyShowEntity = HoverEvent.ShowEntity.showEntity(Key.key("geysermc", "dummyshowitem"),
|
||||
UUID.nameUUIDFromBytes("entitiesareprettyneat".getBytes(StandardCharsets.UTF_8)));
|
||||
dummyShowItem = HoverEvent.ShowItem.of(Key.key("geysermc", "dummyshowentity"), 0);
|
||||
dummyShowItem = HoverEvent.ShowItem.showItem(Key.key("geysermc", "dummyshowentity"), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -43,7 +43,7 @@ public class CommandBlockBlockEntityTranslator extends BlockEntityTranslator imp
|
|||
// Java and Bedrock values
|
||||
builder.put("conditionMet", ((ByteTag) tag.get("conditionMet")).getValue());
|
||||
builder.put("auto", ((ByteTag) tag.get("auto")).getValue());
|
||||
builder.put("CustomName", MessageTranslator.convertMessage(((StringTag) tag.get("CustomName")).getValue()));
|
||||
builder.put("CustomName", MessageTranslator.convertJsonMessage(((StringTag) tag.get("CustomName")).getValue()));
|
||||
builder.put("powered", ((ByteTag) tag.get("powered")).getValue());
|
||||
builder.put("Command", ((StringTag) tag.get("Command")).getValue());
|
||||
builder.put("SuccessCount", ((IntTag) tag.get("SuccessCount")).getValue());
|
||||
|
|
|
@ -28,7 +28,9 @@ package org.geysermc.geyser.translator.text;
|
|||
import com.github.steveice10.mc.protocol.data.DefaultComponentSerializer;
|
||||
import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamColor;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ScoreComponent;
|
||||
import net.kyori.adventure.text.TranslatableComponent;
|
||||
import net.kyori.adventure.text.flattener.ComponentFlattener;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import net.kyori.adventure.text.renderer.TranslatableComponentRenderer;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
|
@ -50,8 +52,8 @@ public class MessageTranslator {
|
|||
// Possible TODO: replace the legacy hover event serializer with an empty one since we have no use for hover events
|
||||
private static final GsonComponentSerializer GSON_SERIALIZER;
|
||||
|
||||
private static final LegacyComponentSerializer LEGACY_SERIALIZER;
|
||||
private static final String ALL_COLORS;
|
||||
private static final LegacyComponentSerializer BEDROCK_SERIALIZER;
|
||||
private static final String BEDROCK_COLORS;
|
||||
|
||||
// Store team colors for player names
|
||||
private static final Map<TeamColor, String> TEAM_COLORS = new EnumMap<>(TeamColor.class);
|
||||
|
@ -99,47 +101,43 @@ public class MessageTranslator {
|
|||
// Tell MCProtocolLib to use this serializer, too.
|
||||
DefaultComponentSerializer.set(GSON_SERIALIZER);
|
||||
|
||||
LegacyComponentSerializer legacySerializer;
|
||||
String allColors;
|
||||
try {
|
||||
Class.forName("net.kyori.adventure.text.serializer.legacy.CharacterAndFormat");
|
||||
// Customize the formatting characters of our legacy serializer for bedrock edition
|
||||
List<CharacterAndFormat> formats = new ArrayList<>(CharacterAndFormat.defaults());
|
||||
// The following two do not yet exist on Bedrock - https://bugs.mojang.com/browse/MCPE-41729
|
||||
formats.remove(CharacterAndFormat.STRIKETHROUGH);
|
||||
formats.remove(CharacterAndFormat.UNDERLINED);
|
||||
|
||||
List<CharacterAndFormat> formats = new ArrayList<>(CharacterAndFormat.defaults());
|
||||
// The following two do not yet exist on Bedrock - https://bugs.mojang.com/browse/MCPE-41729
|
||||
formats.remove(CharacterAndFormat.STRIKETHROUGH);
|
||||
formats.remove(CharacterAndFormat.UNDERLINED);
|
||||
formats.add(CharacterAndFormat.characterAndFormat('g', TextColor.color(221, 214, 5))); // Minecoin Gold
|
||||
// Add the new characters implemented in 1.19.80
|
||||
formats.add(CharacterAndFormat.characterAndFormat('h', TextColor.color(227, 212, 209))); // Quartz
|
||||
formats.add(CharacterAndFormat.characterAndFormat('i', TextColor.color(206, 202, 202))); // Iron
|
||||
formats.add(CharacterAndFormat.characterAndFormat('j', TextColor.color(68, 58, 59))); // Netherite
|
||||
formats.add(CharacterAndFormat.characterAndFormat('m', TextColor.color(151, 22, 7))); // Redstone
|
||||
formats.add(CharacterAndFormat.characterAndFormat('n', TextColor.color(180, 104, 77))); // Copper
|
||||
formats.add(CharacterAndFormat.characterAndFormat('p', TextColor.color(222, 177, 45))); // Gold
|
||||
formats.add(CharacterAndFormat.characterAndFormat('q', TextColor.color(17, 160, 54))); // Emerald
|
||||
formats.add(CharacterAndFormat.characterAndFormat('s', TextColor.color(44, 186, 168))); // Diamond
|
||||
formats.add(CharacterAndFormat.characterAndFormat('t', TextColor.color(33, 73, 123))); // Lapis
|
||||
formats.add(CharacterAndFormat.characterAndFormat('u', TextColor.color(154, 92, 198))); // Amethyst
|
||||
|
||||
formats.add(CharacterAndFormat.characterAndFormat('g', TextColor.color(221, 214, 5))); // Minecoin Gold
|
||||
// Add the new characters implemented in 1.19.80
|
||||
formats.add(CharacterAndFormat.characterAndFormat('h', TextColor.color(227, 212, 209))); // Quartz
|
||||
formats.add(CharacterAndFormat.characterAndFormat('i', TextColor.color(206, 202, 202))); // Iron
|
||||
formats.add(CharacterAndFormat.characterAndFormat('j', TextColor.color(68, 58, 59))); // Netherite
|
||||
formats.add(CharacterAndFormat.characterAndFormat('m', TextColor.color(151, 22, 7))); // Redstone
|
||||
formats.add(CharacterAndFormat.characterAndFormat('n', TextColor.color(180, 104, 77))); // Copper
|
||||
formats.add(CharacterAndFormat.characterAndFormat('p', TextColor.color(222, 177, 45))); // Gold
|
||||
formats.add(CharacterAndFormat.characterAndFormat('q', TextColor.color(17, 160, 54))); // Emerald
|
||||
formats.add(CharacterAndFormat.characterAndFormat('s', TextColor.color(44, 186, 168))); // Diamond
|
||||
formats.add(CharacterAndFormat.characterAndFormat('t', TextColor.color(33, 73, 123))); // Lapis
|
||||
formats.add(CharacterAndFormat.characterAndFormat('u', TextColor.color(154, 92, 198))); // Amethyst
|
||||
// Can be removed once Adventure 1.15.0 is released (see https://github.com/KyoriPowered/adventure/pull/954)
|
||||
ComponentFlattener flattener = ComponentFlattener.basic().toBuilder()
|
||||
.mapper(ScoreComponent.class, component -> "")
|
||||
.build();
|
||||
|
||||
legacySerializer = LegacyComponentSerializer.legacySection().toBuilder()
|
||||
.formats(formats)
|
||||
.build();
|
||||
BEDROCK_SERIALIZER = LegacyComponentSerializer.legacySection().toBuilder()
|
||||
.formats(formats)
|
||||
.flattener(flattener)
|
||||
.build();
|
||||
|
||||
StringBuilder colorBuilder = new StringBuilder();
|
||||
for (CharacterAndFormat format : formats) {
|
||||
if (format.format() instanceof TextColor) {
|
||||
colorBuilder.append(format.character());
|
||||
}
|
||||
// cache all the legacy character codes
|
||||
StringBuilder colorBuilder = new StringBuilder();
|
||||
for (CharacterAndFormat format : formats) {
|
||||
if (format.format() instanceof TextColor) {
|
||||
colorBuilder.append(format.character());
|
||||
}
|
||||
allColors = colorBuilder.toString();
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
// Velocity doesn't have this yet.
|
||||
legacySerializer = LegacyComponentSerializer.legacySection();
|
||||
allColors = "0123456789abcdef";
|
||||
}
|
||||
LEGACY_SERIALIZER = legacySerializer;
|
||||
ALL_COLORS = allColors;
|
||||
BEDROCK_COLORS = colorBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -154,7 +152,7 @@ public class MessageTranslator {
|
|||
// Translate any components that require it
|
||||
message = RENDERER.render(message, locale);
|
||||
|
||||
String legacy = LEGACY_SERIALIZER.serialize(message);
|
||||
String legacy = BEDROCK_SERIALIZER.serialize(message);
|
||||
|
||||
StringBuilder finalLegacy = new StringBuilder();
|
||||
char[] legacyChars = legacy.toCharArray();
|
||||
|
@ -170,7 +168,7 @@ public class MessageTranslator {
|
|||
}
|
||||
|
||||
char next = legacyChars[++i];
|
||||
if (ALL_COLORS.indexOf(next) != -1) {
|
||||
if (BEDROCK_COLORS.indexOf(next) != -1) {
|
||||
// Append this color code, as well as a necessary reset code
|
||||
if (!lastFormatReset) {
|
||||
finalLegacy.append(RESET);
|
||||
|
@ -189,12 +187,12 @@ public class MessageTranslator {
|
|||
}
|
||||
}
|
||||
|
||||
public static String convertMessage(String message, String locale) {
|
||||
public static String convertJsonMessage(String message, String locale) {
|
||||
return convertMessage(GSON_SERIALIZER.deserialize(message), locale);
|
||||
}
|
||||
|
||||
public static String convertMessage(String message) {
|
||||
return convertMessage(message, GeyserLocale.getDefaultLocale());
|
||||
public static String convertJsonMessage(String message) {
|
||||
return convertJsonMessage(message, GeyserLocale.getDefaultLocale());
|
||||
}
|
||||
|
||||
public static String convertMessage(Component message) {
|
||||
|
@ -202,7 +200,7 @@ public class MessageTranslator {
|
|||
}
|
||||
|
||||
/**
|
||||
* Verifies the message is valid JSON in case it's plaintext. Works around GsonComponentSeraializer not using lenient mode.
|
||||
* Verifies the message is valid JSON in case it's plaintext. Works around GsonComponentSerializer not using lenient mode.
|
||||
* See https://wiki.vg/Chat for messages sent in lenient mode, and for a description on leniency.
|
||||
*
|
||||
* @param message Potentially lenient JSON message
|
||||
|
@ -218,9 +216,10 @@ public class MessageTranslator {
|
|||
}
|
||||
|
||||
try {
|
||||
return convertMessage(message, locale);
|
||||
return convertJsonMessage(message, locale);
|
||||
} catch (Exception ignored) {
|
||||
String convertedMessage = convertMessage(convertToJavaMessage(message), locale);
|
||||
// Use the default legacy serializer since message is java-legacy
|
||||
String convertedMessage = convertMessage(LegacyComponentSerializer.legacySection().deserialize(message), locale);
|
||||
|
||||
// We have to do this since Adventure strips the starting reset character
|
||||
if (message.startsWith(RESET) && !convertedMessage.startsWith(RESET)) {
|
||||
|
@ -242,11 +241,10 @@ public class MessageTranslator {
|
|||
* @return The formatted JSON string
|
||||
*/
|
||||
public static String convertToJavaMessage(String message) {
|
||||
Component component = LegacyComponentSerializer.legacySection().deserialize(message);
|
||||
Component component = BEDROCK_SERIALIZER.deserialize(message);
|
||||
return GSON_SERIALIZER.serialize(component);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert legacy format message to plain text
|
||||
*
|
||||
|
@ -275,7 +273,7 @@ public class MessageTranslator {
|
|||
* @param locale Locale to use for translation strings
|
||||
* @return The plain text of the message
|
||||
*/
|
||||
public static String convertToPlainText(String message, String locale) {
|
||||
public static String convertToPlainTextLenient(String message, String locale) {
|
||||
if (message == null) {
|
||||
return "";
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ import java.util.Map;
|
|||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
public class MessageTranslatorTest {
|
||||
|
||||
private Map<String, String> messages = new HashMap<>();
|
||||
private final Map<String, String> messages = new HashMap<>();
|
||||
|
||||
@BeforeAll
|
||||
public void setUp() throws Exception {
|
||||
|
@ -70,7 +70,7 @@ public class MessageTranslatorTest {
|
|||
@Test
|
||||
public void convertMessage() {
|
||||
for (Map.Entry<String, String> entry : messages.entrySet()) {
|
||||
String bedrockMessage = MessageTranslator.convertMessage(entry.getKey(), "en_US");
|
||||
String bedrockMessage = MessageTranslator.convertJsonMessage(entry.getKey(), "en_US");
|
||||
Assertions.assertEquals(entry.getValue(), bedrockMessage, "Translation of messages is incorrect");
|
||||
}
|
||||
}
|
||||
|
@ -85,13 +85,13 @@ public class MessageTranslatorTest {
|
|||
|
||||
@Test
|
||||
public void convertToPlainText() {
|
||||
Assertions.assertEquals("Many colors here", MessageTranslator.convertToPlainText("{\"extra\":[{\"color\":\"red\",\"text\":\"M\"},{\"color\":\"gold\",\"text\":\"a\"},{\"color\":\"yellow\",\"text\":\"n\"},{\"color\":\"green\",\"text\":\"y \"},{\"color\":\"aqua\",\"text\":\"c\"},{\"color\":\"dark_purple\",\"text\":\"o\"},{\"color\":\"red\",\"text\":\"l\"},{\"color\":\"gold\",\"text\":\"o\"},{\"color\":\"yellow\",\"text\":\"r\"},{\"color\":\"green\",\"text\":\"s \"},{\"color\":\"aqua\",\"text\":\"h\"},{\"color\":\"dark_purple\",\"text\":\"e\"},{\"color\":\"red\",\"text\":\"r\"},{\"color\":\"gold\",\"text\":\"e\"}],\"text\":\"\"}", "en_US"), "JSON message is not handled properly");
|
||||
Assertions.assertEquals("Many colors here", MessageTranslator.convertToPlainTextLenient("{\"extra\":[{\"color\":\"red\",\"text\":\"M\"},{\"color\":\"gold\",\"text\":\"a\"},{\"color\":\"yellow\",\"text\":\"n\"},{\"color\":\"green\",\"text\":\"y \"},{\"color\":\"aqua\",\"text\":\"c\"},{\"color\":\"dark_purple\",\"text\":\"o\"},{\"color\":\"red\",\"text\":\"l\"},{\"color\":\"gold\",\"text\":\"o\"},{\"color\":\"yellow\",\"text\":\"r\"},{\"color\":\"green\",\"text\":\"s \"},{\"color\":\"aqua\",\"text\":\"h\"},{\"color\":\"dark_purple\",\"text\":\"e\"},{\"color\":\"red\",\"text\":\"r\"},{\"color\":\"gold\",\"text\":\"e\"}],\"text\":\"\"}", "en_US"), "JSON message is not handled properly");
|
||||
Assertions.assertEquals("Many colors here", MessageTranslator.convertToPlainText("§cM§6a§en§ay §bc§5o§cl§6o§er§as §bh§5e§cr§6e"), "Legacy formatted message is not handled properly (Colors)");
|
||||
Assertions.assertEquals("Many colors here", MessageTranslator.convertToPlainText("§cM§6a§en§ay §bc§5o§cl§6o§er§as §bh§5e§cr§6e", "en_US"), "Legacy formatted message is not handled properly (Colors)");
|
||||
Assertions.assertEquals("Obf Bold Strikethrough Underline Italic Reset", MessageTranslator.convertToPlainText("§kObf §lBold §mStrikethrough §nUnderline §oItalic §rReset", "en_US"), "Legacy formatted message is not handled properly (Style)");
|
||||
Assertions.assertEquals("Strange", MessageTranslator.convertToPlainText("§rStrange", "en_US"), "Valid lenient JSON is not handled properly");
|
||||
Assertions.assertEquals("", MessageTranslator.convertToPlainText("", "en_US"), "Empty message is not handled properly");
|
||||
Assertions.assertEquals(" ", MessageTranslator.convertToPlainText(" ", "en_US"), "Whitespace is not preserved");
|
||||
Assertions.assertEquals("Many colors here", MessageTranslator.convertToPlainTextLenient("§cM§6a§en§ay §bc§5o§cl§6o§er§as §bh§5e§cr§6e", "en_US"), "Legacy formatted message is not handled properly (Colors)");
|
||||
Assertions.assertEquals("Obf Bold Strikethrough Underline Italic Reset", MessageTranslator.convertToPlainTextLenient("§kObf §lBold §mStrikethrough §nUnderline §oItalic §rReset", "en_US"), "Legacy formatted message is not handled properly (Style)");
|
||||
Assertions.assertEquals("Strange", MessageTranslator.convertToPlainTextLenient("§rStrange", "en_US"), "Valid lenient JSON is not handled properly");
|
||||
Assertions.assertEquals("", MessageTranslator.convertToPlainTextLenient("", "en_US"), "Empty message is not handled properly");
|
||||
Assertions.assertEquals(" ", MessageTranslator.convertToPlainTextLenient(" ", "en_US"), "Whitespace is not preserved");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -14,8 +14,8 @@ protocol-connection = "3.0.0.Beta1-20230718.000033-101"
|
|||
raknet = "1.0.0.CR1-20230703.195238-9"
|
||||
mcauthlib = "d9d773e"
|
||||
mcprotocollib = "1.20-1-20230607.135651-6" # Temporary hack - needs to be updated to release once publishing is fixed
|
||||
adventure = "4.14.0-20230424.215040-7"
|
||||
adventure-platform = "4.1.2"
|
||||
adventure = "4.14.0"
|
||||
adventure-platform = "4.3.0"
|
||||
junit = "5.9.2"
|
||||
checkerframework = "3.19.0"
|
||||
log4j = "2.20.0"
|
||||
|
@ -26,7 +26,7 @@ viaversion = "4.0.0"
|
|||
adapters = "1.9-SNAPSHOT"
|
||||
commodore = "2.2"
|
||||
bungeecord = "a7c6ede"
|
||||
velocity = "3.0.0"
|
||||
velocity = "3.1.1"
|
||||
sponge = "8.0.0"
|
||||
fabric-minecraft = "1.20"
|
||||
fabric-loader = "0.14.21"
|
||||
|
|
Loading…
Reference in a new issue