Fix GUI colors and TAB placeholder format - applyGradient uses &#RRGGBB for GUI (LegacyComponentSerializer) - Added applyGradientTabFormat for TAB plugin (§x§R§R§G§G§B§B) - PlaceholderAPIHook now uses TAB format for %lochat_full% - Updated tests to match new formats

This commit is contained in:
loki5512344 2025-12-30 21:54:34 +01:00
parent 036382b71a
commit 29ae373554
5 changed files with 127 additions and 26 deletions

View file

@ -116,8 +116,25 @@ public class GradientModule {
/** /**
* Получает отформатированное имя игрока с градиентом и LuckPerms префиксом * Получает отформатированное имя игрока с градиентом и LuckPerms префиксом
* Градиент применяется на всю строку целиком (префикс + ник) * Градиент применяется на всю строку целиком (префикс + ник)
* Использует формат &#RRGGBB
*/ */
public String getFormattedName(Player player) { public String getFormattedName(Player player) {
return getFormattedName(player, false);
}
/**
* Получает отформатированное имя для TAB плагина
* Использует формат §x§R§R§G§G§B§B
*/
public String getFormattedNameForTab(Player player) {
return getFormattedName(player, true);
}
/**
* Получает отформатированное имя игрока с градиентом и LuckPerms префиксом
* @param useTabFormat true для §x§... формата (TAB), false для &#RRGGBB
*/
private String getFormattedName(Player player, boolean useTabFormat) {
if (!enabled) return player.getName(); if (!enabled) return player.getName();
GradientPlayerData data = dataManager.getPlayerData(player.getUniqueId()); GradientPlayerData data = dataManager.getPlayerData(player.getUniqueId());
@ -142,7 +159,7 @@ public class GradientModule {
} else { } else {
// LP префикс со своими цветами, ник отдельно // LP префикс со своими цветами, ник отдельно
String nick = data.hasColors() && data.isColorEnabled() String nick = data.hasColors() && data.isColorEnabled()
? GradientUtil.applyGradient(player.getName(), data.getColors(), config.isUseLegacyRgbFormat()) ? applyGradientWithFormat(player.getName(), data.getColors(), useTabFormat)
: player.getName(); : player.getName();
return lpPrefix + nick; return lpPrefix + nick;
} }
@ -153,12 +170,25 @@ public class GradientModule {
// Применяем единый градиент на всю строку // Применяем единый градиент на всю строку
if (data.hasColors() && data.isColorEnabled()) { if (data.hasColors() && data.isColorEnabled()) {
return GradientUtil.applyGradient(fullText.toString(), data.getColors(), config.isUseLegacyRgbFormat()); return applyGradientWithFormat(fullText.toString(), data.getColors(), useTabFormat);
} }
return fullText.toString(); return fullText.toString();
} }
/**
* Применяет градиент с нужным форматом
*/
private String applyGradientWithFormat(String text, java.util.List<String> colors, boolean useTabFormat) {
if (useTabFormat) {
// Формат §x§R§R§G§G§B§B для TAB
return GradientUtil.applyGradientTabFormat(text, colors);
} else {
// Формат &#RRGGBB для GUI и чата
return GradientUtil.applyGradient(text, colors, true);
}
}
/** /**
* Убирает цветовые коды из строки * Убирает цветовые коды из строки
*/ */

View file

@ -68,8 +68,8 @@ public class GradientConfirmGUI implements InventoryHolder {
skullMeta.setOwningPlayer(player); skullMeta.setOwningPlayer(player);
String preview = buildPreview(); String preview = buildPreview();
// Preview уже в MiniMessage формате, просто парсим // Для GUI используем LegacyComponentSerializer с поддержкой HEX
Component previewComponent = ChatFormatter.parse(preview); Component previewComponent = SERIALIZER.deserialize(preview);
skullMeta.displayName(previewComponent.decoration(TextDecoration.ITALIC, false)); skullMeta.displayName(previewComponent.decoration(TextDecoration.ITALIC, false));
List<Component> lore = new ArrayList<>(); List<Component> lore = new ArrayList<>();
@ -126,22 +126,22 @@ public class GradientConfirmGUI implements InventoryHolder {
} }
} }
// Для GUI всегда используем MiniMessage формат (false) для правильного отображения градиента // Для GUI используем legacy формат &#RRGGBB который LegacyComponentSerializer понимает
boolean useLegacyForPreview = false; boolean useLegacyForPreview = true;
// Если нет кастомного префикса - используем LuckPerms префикс // Если нет кастомного префикса - используем LuckPerms префикс
if ((prefixToUse == null || prefixToUse.isEmpty()) && module.getLuckPermsHook().isEnabled()) { if ((prefixToUse == null || prefixToUse.isEmpty()) && module.getLuckPermsHook().isEnabled()) {
String lpPrefix = module.getLuckPermsHook().getActivePrefix(player); String lpPrefix = module.getLuckPermsHook().getActivePrefix(player);
if (lpPrefix != null && !lpPrefix.isEmpty()) { if (lpPrefix != null && !lpPrefix.isEmpty()) {
// Для превью показываем LP префикс // Конвертируем LP префикс в legacy формат
lpPrefix = convertToLegacy(lpPrefix);
// Если градиент на LP префикс включен - применяем градиент на всю строку // Если градиент на LP префикс включен - применяем градиент на всю строку
if (colorsToUse != null && !colorsToUse.isEmpty() && module.getConfig().isGradientOnLuckPermsPrefix()) { if (colorsToUse != null && !colorsToUse.isEmpty() && module.getConfig().isGradientOnLuckPermsPrefix()) {
// Убираем цвета из LP префикса и применяем градиент на всё
String cleanLpPrefix = stripColors(lpPrefix); String cleanLpPrefix = stripColors(lpPrefix);
String fullText = cleanLpPrefix + nick; String fullText = cleanLpPrefix + nick;
return GradientUtil.applyGradient(fullText, colorsToUse, useLegacyForPreview); return GradientUtil.applyGradient(fullText, colorsToUse, useLegacyForPreview);
} else { } else {
// LP префикс со своими цветами + градиентный ник
if (colorsToUse != null && !colorsToUse.isEmpty()) { if (colorsToUse != null && !colorsToUse.isEmpty()) {
return lpPrefix + GradientUtil.applyGradient(nick, colorsToUse, useLegacyForPreview); return lpPrefix + GradientUtil.applyGradient(nick, colorsToUse, useLegacyForPreview);
} }
@ -161,6 +161,15 @@ public class GradientConfirmGUI implements InventoryHolder {
); );
} }
/**
* Конвертирует MiniMessage теги в legacy формат
*/
private String convertToLegacy(String text) {
if (text == null) return "";
// Конвертируем <#RRGGBB> в &#RRGGBB
return text.replaceAll("<#([A-Fa-f0-9]{6})>", "&#$1");
}
/** /**
* Убирает цветовые коды из строки * Убирает цветовые коды из строки
*/ */

View file

@ -98,15 +98,57 @@ public final class GradientUtil {
private static String formatHex(String hex, boolean useLegacyFormat) { private static String formatHex(String hex, boolean useLegacyFormat) {
hex = hex.replace("#", "").toLowerCase(); hex = hex.replace("#", "").toLowerCase();
if (useLegacyFormat) { if (useLegacyFormat) {
// Формат §x§R§R§G§G§B§B для TAB и других плагинов // Формат &#RRGGBB для LegacyComponentSerializer
return "&#" + hex;
} else {
return "<#" + hex + ">";
}
}
/**
* Конвертирует HEX в формат §x§R§R§G§G§B§B для TAB плагина
*/
private static String formatHexForTab(String hex) {
hex = hex.replace("#", "").toLowerCase();
StringBuilder sb = new StringBuilder("§x"); StringBuilder sb = new StringBuilder("§x");
for (char c : hex.toCharArray()) { for (char c : hex.toCharArray()) {
sb.append("§").append(c); sb.append("§").append(c);
} }
return sb.toString(); return sb.toString();
} else {
return "<#" + hex + ">";
} }
/**
* Применяет градиент в формате §x§R§R§G§G§B§B для TAB плагина
*/
public static String applyGradientTabFormat(String text, List<String> hexColors) {
if (text == null || text.isEmpty() || hexColors == null || hexColors.isEmpty()) {
return text;
}
char[] chars = text.toCharArray();
int length = chars.length;
if (length == 0) return text;
StringBuilder result = new StringBuilder();
if (hexColors.size() == 1) {
String colorCode = formatHexForTab(hexColors.get(0));
result.append(colorCode).append(text);
} else {
Color[] colors = hexColors.stream()
.map(GradientUtil::parseHex)
.toArray(Color[]::new);
for (int i = 0; i < length; i++) {
double ratio = length > 1 ? (double) i / (length - 1) : 0.0;
Color interpolated = interpolateMultiColor(colors, ratio);
String hex = String.format("#%02x%02x%02x",
interpolated.getRed(), interpolated.getGreen(), interpolated.getBlue());
result.append(formatHexForTab(hex)).append(chars[i]);
}
}
return result.toString();
} }
public static String buildDisplayName(String prefix, String nick, List<String> colors, public static String buildDisplayName(String prefix, String nick, List<String> colors,

View file

@ -69,6 +69,7 @@ public class PlaceholderAPIHook extends PlaceholderExpansion {
} }
// Gradient placeholders (совместимость с LoPreff) // Gradient placeholders (совместимость с LoPreff)
// Используем TAB формат §x§R§R§G§G§B§B для совместимости с TAB плагином
case "gradient_full", "full" -> { case "gradient_full", "full" -> {
Player onlinePlayer = player.getPlayer(); Player onlinePlayer = player.getPlayer();
if (onlinePlayer == null) yield player.getName() != null ? player.getName() : ""; if (onlinePlayer == null) yield player.getName() != null ? player.getName() : "";
@ -78,7 +79,8 @@ public class PlaceholderAPIHook extends PlaceholderExpansion {
yield player.getName() != null ? player.getName() : ""; yield player.getName() != null ? player.getName() : "";
} }
yield gradient.getFormattedName(onlinePlayer); // Используем TAB формат для плейсхолдеров
yield gradient.getFormattedNameForTab(onlinePlayer);
} }
case "gradient_name", "name" -> { case "gradient_name", "name" -> {
Player onlinePlayer = player.getPlayer(); Player onlinePlayer = player.getPlayer();

View file

@ -35,8 +35,8 @@ class GradientUtilTest {
void applyGradient_singleColor_legacy() { void applyGradient_singleColor_legacy() {
String result = GradientUtil.applyGradient("Hi", List.of("#FF0000"), true); String result = GradientUtil.applyGradient("Hi", List.of("#FF0000"), true);
assertNotNull(result); assertNotNull(result);
// Формат §x§f§f§0§0§0§0 // Формат &#RRGGBB для LegacyComponentSerializer
assertTrue(result.contains("§x§f§f§0§0§0§0")); assertTrue(result.contains("&#ff0000"));
} }
@Test @Test
@ -50,9 +50,9 @@ class GradientUtilTest {
void applyGradient_twoColors() { void applyGradient_twoColors() {
String result = GradientUtil.applyGradient("AB", List.of("#FF0000", "#0000FF"), true); String result = GradientUtil.applyGradient("AB", List.of("#FF0000", "#0000FF"), true);
assertNotNull(result); assertNotNull(result);
// Первый символ должен быть красным, последний синим // Первый символ должен быть красным, последний синим (формат &#RRGGBB)
assertTrue(result.contains("§x§f§f§0§0§0§0")); // красный assertTrue(result.contains("&#ff0000")); // красный
assertTrue(result.contains("§x§0§0§0§0§f§f")); // синий assertTrue(result.contains("&#0000ff")); // синий
} }
@Test @Test
@ -105,23 +105,24 @@ class GradientUtilTest {
void buildDisplayName_noPrefix_withColors() { void buildDisplayName_noPrefix_withColors() {
String result = GradientUtil.buildDisplayName(null, "AB", List.of("#FF0000", "#0000FF"), true, true, "[{prefix}] ", true); String result = GradientUtil.buildDisplayName(null, "AB", List.of("#FF0000", "#0000FF"), true, true, "[{prefix}] ", true);
assertNotNull(result); assertNotNull(result);
assertTrue(result.contains("§x")); // Формат &#RRGGBB
assertTrue(result.contains("&#"));
} }
@Test @Test
void buildDisplayName_withPrefix_withColors_continuous() { void buildDisplayName_withPrefix_withColors_continuous() {
String result = GradientUtil.buildDisplayName("V", "AB", List.of("#FF0000", "#0000FF"), true, true, "[{prefix}] ", true); String result = GradientUtil.buildDisplayName("V", "AB", List.of("#FF0000", "#0000FF"), true, true, "[{prefix}] ", true);
assertNotNull(result); assertNotNull(result);
// Единый градиент на всё // Единый градиент на всё (формат &#RRGGBB)
assertTrue(result.contains("§x")); assertTrue(result.contains("&#"));
} }
@Test @Test
void buildDisplayName_withPrefix_withColors_separate() { void buildDisplayName_withPrefix_withColors_separate() {
String result = GradientUtil.buildDisplayName("V", "AB", List.of("#FF0000", "#0000FF"), true, false, "[{prefix}] ", true); String result = GradientUtil.buildDisplayName("V", "AB", List.of("#FF0000", "#0000FF"), true, false, "[{prefix}] ", true);
assertNotNull(result); assertNotNull(result);
// Отдельные градиенты // Отдельные градиенты (формат &#RRGGBB)
assertTrue(result.contains("§x")); assertTrue(result.contains("&#"));
} }
@Test @Test
@ -131,4 +132,21 @@ class GradientUtilTest {
// Префикс без градиента // Префикс без градиента
assertTrue(result.startsWith("[VIP] ")); assertTrue(result.startsWith("[VIP] "));
} }
@Test
void applyGradientTabFormat_singleColor() {
String result = GradientUtil.applyGradientTabFormat("Hi", List.of("#FF0000"));
assertNotNull(result);
// Формат §x§f§f§0§0§0§0 для TAB
assertTrue(result.contains("§x§f§f§0§0§0§0"));
}
@Test
void applyGradientTabFormat_twoColors() {
String result = GradientUtil.applyGradientTabFormat("AB", List.of("#FF0000", "#0000FF"));
assertNotNull(result);
// Первый символ красный, последний синий
assertTrue(result.contains("§x§f§f§0§0§0§0")); // красный
assertTrue(result.contains("§x§0§0§0§0§f§f")); // синий
}
} }