Skip to content

Commit

Permalink
[MiniMessage] Add support for its translatable components (paper / ve…
Browse files Browse the repository at this point in the history
…locity only) (#1399)
  • Loading branch information
NEZNAMY committed Dec 13, 2024
1 parent 1c4f404 commit b89bf59
Show file tree
Hide file tree
Showing 12 changed files with 332 additions and 187 deletions.
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
package me.neznamy.tab.platforms.paper;

import me.neznamy.tab.platforms.bukkit.nms.converter.ComponentConverter;
import me.neznamy.tab.shared.chat.ChatModifier;
import me.neznamy.tab.shared.chat.SimpleComponent;
import me.neznamy.tab.shared.chat.StructuredComponent;
import me.neznamy.tab.shared.chat.TabComponent;
import net.minecraft.ChatFormatting;
import me.neznamy.tab.shared.chat.*;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.format.TextDecoration;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.Style;
import net.minecraft.network.chat.TextColor;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;
import org.jetbrains.annotations.Nullable;

/**
* Component converter using direct mojang-mapped code for versions 1.20.5+.
Expand All @@ -25,38 +23,71 @@ public class PaperComponentConverter extends ComponentConverter {
@Override
@NotNull
public Component convert(@NotNull TabComponent component, boolean modern) {
if (component instanceof SimpleComponent) return Component.literal(((SimpleComponent) component).getText());

StructuredComponent component1 = (StructuredComponent) component;
MutableComponent nmsComponent = Component.literal(component1.getText());
nmsComponent.setStyle(createModifierModern(component1.getModifier(), modern));
for (StructuredComponent extra : component1.getExtra()) {
nmsComponent.append(convert(extra, modern));
switch (component) {
case SimpleComponent simpleComponent -> {
return Component.literal(simpleComponent.getText());
}
case StructuredComponent component1 -> {
MutableComponent nmsComponent = Component.literal(component1.getText());
ChatModifier modifier = component1.getModifier();
TextColor color = null;
if (modifier.getColor() != null) {
if (modern) {
color = TextColor.fromRgb(modifier.getColor().getRgb());
} else {
color = TextColor.fromRgb(modifier.getColor().getLegacyColor().getRgb());
}
}
nmsComponent.setStyle(newStyle(color, modifier.isBold(), modifier.isItalic(), modifier.isUnderlined(),
modifier.isUnderlined(), modifier.isObfuscated(), modifier.getFont()));
for (StructuredComponent extra : component1.getExtra()) {
nmsComponent.append(convert(extra, modern));
}
return nmsComponent;
}
case AdventureComponent component1 -> {
return fromAdventure(component1.getComponent());
}
default -> throw new IllegalStateException("Unexpected component type: " + component.getClass().getName());
}
return nmsComponent;
}

@NotNull
private Style createModifierModern(@NotNull ChatModifier modifier, boolean modern) {
TextColor color = null;
if (modifier.getColor() != null) {
if (modern) {
color = TextColor.fromRgb(modifier.getColor().getRgb());
} else {
color = TextColor.fromRgb(modifier.getColor().getLegacyColor().getRgb());
}
private Component fromAdventure(@NotNull net.kyori.adventure.text.Component component) {
MutableComponent nmsComponent;
if (component instanceof TextComponent component1) {
nmsComponent = Component.literal(component1.content());
} else if (component instanceof TranslatableComponent component1) {
nmsComponent = Component.translatable(component1.key());
} else throw new IllegalStateException("Cannot convert " + component.getClass().getName());

net.kyori.adventure.text.format.TextColor color = component.color();
Key font = component.style().font();
nmsComponent.setStyle(newStyle(
color == null ? null : TextColor.fromRgb(color.value()),
component.style().hasDecoration(TextDecoration.BOLD),
component.style().hasDecoration(TextDecoration.ITALIC),
component.style().hasDecoration(TextDecoration.UNDERLINED),
component.style().hasDecoration(TextDecoration.STRIKETHROUGH),
component.style().hasDecoration(TextDecoration.OBFUSCATED),
font == null ? null : font.asString()
));
for (net.kyori.adventure.text.Component extra : component.children()) {
nmsComponent.append(fromAdventure(extra));
}
List<ChatFormatting> formats = new ArrayList<>();
if (modifier.isBold()) formats.add(ChatFormatting.BOLD);
if (modifier.isItalic()) formats.add(ChatFormatting.ITALIC);
if (modifier.isUnderlined()) formats.add(ChatFormatting.UNDERLINE);
if (modifier.isStrikethrough()) formats.add(ChatFormatting.STRIKETHROUGH);
if (modifier.isObfuscated()) formats.add(ChatFormatting.OBFUSCATED);
return nmsComponent;
}

Style style = Style.EMPTY;
if (color != null) style = style.withColor(color);
if (!formats.isEmpty()) style = style.applyFormats(formats.toArray(new ChatFormatting[0]));
if (modifier.getFont() != null) style = style.withFont(ResourceLocation.tryParse(modifier.getFont()));
return style;
@NotNull
private Style newStyle(@Nullable TextColor color, boolean bold, boolean italic, boolean underlined,
boolean strikethrough, boolean obfuscated, @Nullable String font) {
return Style.EMPTY
.withColor(color)
.withBold(bold)
.withItalic(italic)
.withUnderlined(underlined)
.withStrikethrough(strikethrough)
.withObfuscated(obfuscated)
.withFont(font == null ? null : ResourceLocation.tryParse(font));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

import lombok.SneakyThrows;
import me.neznamy.tab.platforms.bukkit.nms.BukkitReflection;
import me.neznamy.tab.shared.chat.ChatModifier;
import me.neznamy.tab.shared.chat.SimpleComponent;
import me.neznamy.tab.shared.chat.StructuredComponent;
import me.neznamy.tab.shared.chat.TabComponent;
import me.neznamy.tab.shared.chat.*;
import me.neznamy.tab.shared.util.FunctionWithException;
import me.neznamy.tab.shared.util.ReflectionUtils;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.format.TextDecoration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
Expand All @@ -23,6 +25,7 @@
public class ReflectionComponentConverter extends ComponentConverter {

private final FunctionWithException<String, Object> newTextComponent;
private final FunctionWithException<String, Object> newTranslatableComponent;
private final BiFunction<ChatModifier, Boolean, Object> convertModifier;

private final Class<?> ChatModifier = BukkitReflection.getClass("network.chat.Style", "network.chat.ChatModifier", "ChatModifier");
Expand Down Expand Up @@ -50,16 +53,23 @@ public ReflectionComponentConverter() throws ReflectiveOperationException {
if (BukkitReflection.getMinorVersion() >= 19) {
Method IChatBaseComponent_b = ReflectionUtils.getMethod(IChatBaseComponent, new String[] {"b", "literal"}, String.class);
newTextComponent = text -> IChatBaseComponent_b.invoke(null, text);
Method IChatBaseComponent_c = ReflectionUtils.getMethod(IChatBaseComponent, new String[] {"c", "translatable"}, String.class);
newTranslatableComponent = text -> IChatBaseComponent_c.invoke(null, text);
Class<?> IChatMutableComponent = BukkitReflection.getClass("network.chat.MutableComponent", "network.chat.IChatMutableComponent", "IChatMutableComponent");
Component_modifier = ReflectionUtils.getOnlyField(IChatMutableComponent, ChatModifier);
ChatBaseComponent_addSibling = ReflectionUtils.getOnlyMethod(IChatMutableComponent, IChatMutableComponent, IChatBaseComponent);
} else {
Class<?> ChatComponentText = BukkitReflection.getClass("network.chat.TextComponent", "network.chat.ChatComponentText", "ChatComponentText");
Constructor<?> newChatComponentText = ChatComponentText.getConstructor(String.class);
newTextComponent = newChatComponentText::newInstance;

Class<?> ChatMessage = BukkitReflection.getClass("network.chat.TranslatableComponent", "network.chat.ChatMessage", "ChatMessage");
Constructor<?> newChatMessage = ChatMessage.getConstructor(String.class, Object[].class);
newTranslatableComponent = text -> newChatMessage.newInstance(text, new Object[0]);

Class<?> ChatBaseComponent = BukkitReflection.getClass("network.chat.BaseComponent", "network.chat.ChatBaseComponent", "ChatBaseComponent");
Component_modifier = ReflectionUtils.getOnlyField(ChatBaseComponent, ChatModifier);
ChatBaseComponent_addSibling = ReflectionUtils.getOnlyMethod(ChatComponentText, IChatBaseComponent, IChatBaseComponent);
ChatBaseComponent_addSibling = ReflectionUtils.getOnlyMethod(ChatBaseComponent, IChatBaseComponent, IChatBaseComponent);
}
if (BukkitReflection.getMinorVersion() >= 16) {
Class<?> chatHexColor = BukkitReflection.getClass("network.chat.TextColor", "network.chat.ChatHexColor", "ChatHexColor");
Expand Down Expand Up @@ -89,13 +99,44 @@ public ReflectionComponentConverter() throws ReflectiveOperationException {
@SneakyThrows
@NotNull
public Object convert(@NotNull TabComponent component, boolean modern) {
if (component instanceof SimpleComponent) return newTextComponent.apply(((SimpleComponent) component).getText());
if (component instanceof SimpleComponent) {
return newTextComponent.apply(((SimpleComponent) component).getText());
} else if (component instanceof StructuredComponent) {
StructuredComponent component1 = (StructuredComponent) component;
Object nmsComponent = newTextComponent.apply(component1.getText());
Component_modifier.set(nmsComponent, convertModifier.apply(component1.getModifier(), modern));
for (StructuredComponent extra : component1.getExtra()) {
ChatBaseComponent_addSibling.invoke(nmsComponent, convert(extra, modern));
}
return nmsComponent;
} else {
return fromAdventure(((AdventureComponent)component).getComponent());
}
}

StructuredComponent component1 = (StructuredComponent) component;
Object nmsComponent = newTextComponent.apply(component1.getText());
Component_modifier.set(nmsComponent, convertModifier.apply(component1.getModifier(), modern));
for (StructuredComponent extra : component1.getExtra()) {
ChatBaseComponent_addSibling.invoke(nmsComponent, convert(extra, modern));
@SneakyThrows
@NotNull
private Object fromAdventure(@NotNull net.kyori.adventure.text.Component component) {
Object nmsComponent;
if (component instanceof TextComponent) {
nmsComponent = newTextComponent.apply(((TextComponent) component).content());
} else if (component instanceof TranslatableComponent) {
nmsComponent = newTranslatableComponent.apply(((TranslatableComponent)component).key());
} else throw new IllegalStateException("Cannot convert " + component.getClass().getName());

net.kyori.adventure.text.format.TextColor color = component.color();
Key font = component.style().font();
Component_modifier.set(nmsComponent, newStyleModern(
color == null ? null : ChatHexColor_fromRGB.invoke(null, color.value()),
component.style().hasDecoration(TextDecoration.BOLD),
component.style().hasDecoration(TextDecoration.ITALIC),
component.style().hasDecoration(TextDecoration.UNDERLINED),
component.style().hasDecoration(TextDecoration.STRIKETHROUGH),
component.style().hasDecoration(TextDecoration.OBFUSCATED),
font == null ? null : font.asString()
));
for (net.kyori.adventure.text.Component extra : component.children()) {
ChatBaseComponent_addSibling.invoke(nmsComponent, fromAdventure(extra));
}
return nmsComponent;
}
Expand All @@ -110,47 +151,61 @@ private Object createModifierModern(@NotNull ChatModifier modifier, boolean mode
color = ChatHexColor_fromRGB.invoke(null, modifier.getColor().getLegacyColor().getRgb());
}
}
return newStyleModern(
color,
modifier.isBold(),
modifier.isItalic(),
modifier.isUnderlined(),
modifier.isStrikethrough(),
modifier.isObfuscated(),
modifier.getFont());
}

@SneakyThrows
private Object createModifierLegacy(@NotNull ChatModifier modifier) {
Object nmsModifier = newChatModifier.newInstance();
if (modifier.getColor() != null) {
ChatModifier_setColor.invoke(nmsModifier, Enum.valueOf(EnumChatFormat, modifier.getColor().getLegacyColor().name()));
}
if (modifier.isBold()) magicCodes.get(0).set(nmsModifier, true);
if (modifier.isItalic()) magicCodes.get(1).set(nmsModifier, true);
if (modifier.isStrikethrough()) magicCodes.get(2).set(nmsModifier, true);
if (modifier.isUnderlined()) magicCodes.get(3).set(nmsModifier, true);
if (modifier.isObfuscated()) magicCodes.get(4).set(nmsModifier, true);
return nmsModifier;
}

@SneakyThrows
@NotNull
private Object newStyleModern(@Nullable Object color, boolean bold, boolean italic, boolean underlined,
boolean strikethrough, boolean obfuscated, @Nullable String font) {
if (BukkitReflection.is1_21_4Plus()) {
return newChatModifier.newInstance(
color,
0,
modifier.isBold(),
modifier.isItalic(),
modifier.isUnderlined(),
modifier.isStrikethrough(),
modifier.isObfuscated(),
bold,
italic,
underlined,
strikethrough,
obfuscated,
null,
null,
null,
modifier.getFont() == null ? null : ResourceLocation_tryParse.invoke(null, modifier.getFont())
font == null ? null : ResourceLocation_tryParse.invoke(null, font)
);
} else {
return newChatModifier.newInstance(
color,
modifier.isBold(),
modifier.isItalic(),
modifier.isUnderlined(),
modifier.isStrikethrough(),
modifier.isObfuscated(),
bold,
italic,
underlined,
strikethrough,
obfuscated,
null,
null,
null,
modifier.getFont() == null ? null : ResourceLocation_tryParse.invoke(null, modifier.getFont())
font == null ? null : ResourceLocation_tryParse.invoke(null, font)
);
}
}

@SneakyThrows
private Object createModifierLegacy(@NotNull ChatModifier modifier) {
Object nmsModifier = newChatModifier.newInstance();
if (modifier.getColor() != null) {
ChatModifier_setColor.invoke(nmsModifier, Enum.valueOf(EnumChatFormat, modifier.getColor().getLegacyColor().name()));
}
if (modifier.isBold()) magicCodes.get(0).set(nmsModifier, true);
if (modifier.isItalic()) magicCodes.get(1).set(nmsModifier, true);
if (modifier.isStrikethrough()) magicCodes.get(2).set(nmsModifier, true);
if (modifier.isUnderlined()) magicCodes.get(3).set(nmsModifier, true);
if (modifier.isObfuscated()) magicCodes.get(4).set(nmsModifier, true);
return nmsModifier;
}
}
Loading

0 comments on commit b89bf59

Please sign in to comment.