diff --git a/src/main/java/com/loki/lochat/gradient/GradientModule.java b/src/main/java/com/loki/lochat/gradient/GradientModule.java index a182405..b0a41ec 100644 --- a/src/main/java/com/loki/lochat/gradient/GradientModule.java +++ b/src/main/java/com/loki/lochat/gradient/GradientModule.java @@ -116,8 +116,25 @@ public class GradientModule { /** * Получает отформатированное имя игрока с градиентом и LuckPerms префиксом * Градиент применяется на всю строку целиком (префикс + ник) + * Использует формат &#RRGGBB */ 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(); GradientPlayerData data = dataManager.getPlayerData(player.getUniqueId()); @@ -142,7 +159,7 @@ public class GradientModule { } else { // LP префикс со своими цветами, ник отдельно String nick = data.hasColors() && data.isColorEnabled() - ? GradientUtil.applyGradient(player.getName(), data.getColors(), config.isUseLegacyRgbFormat()) + ? applyGradientWithFormat(player.getName(), data.getColors(), useTabFormat) : player.getName(); return lpPrefix + nick; } @@ -153,12 +170,25 @@ public class GradientModule { // Применяем единый градиент на всю строку if (data.hasColors() && data.isColorEnabled()) { - return GradientUtil.applyGradient(fullText.toString(), data.getColors(), config.isUseLegacyRgbFormat()); + return applyGradientWithFormat(fullText.toString(), data.getColors(), useTabFormat); } return fullText.toString(); } + /** + * Применяет градиент с нужным форматом + */ + private String applyGradientWithFormat(String text, java.util.List 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); + } + } + /** * Убирает цветовые коды из строки */ diff --git a/src/main/java/com/loki/lochat/gradient/gui/GradientConfirmGUI.java b/src/main/java/com/loki/lochat/gradient/gui/GradientConfirmGUI.java index 640a254..0311cf1 100644 --- a/src/main/java/com/loki/lochat/gradient/gui/GradientConfirmGUI.java +++ b/src/main/java/com/loki/lochat/gradient/gui/GradientConfirmGUI.java @@ -68,8 +68,8 @@ public class GradientConfirmGUI implements InventoryHolder { skullMeta.setOwningPlayer(player); String preview = buildPreview(); - // Preview уже в MiniMessage формате, просто парсим - Component previewComponent = ChatFormatter.parse(preview); + // Для GUI используем LegacyComponentSerializer с поддержкой HEX + Component previewComponent = SERIALIZER.deserialize(preview); skullMeta.displayName(previewComponent.decoration(TextDecoration.ITALIC, false)); List lore = new ArrayList<>(); @@ -126,22 +126,22 @@ public class GradientConfirmGUI implements InventoryHolder { } } - // Для GUI всегда используем MiniMessage формат (false) для правильного отображения градиента - boolean useLegacyForPreview = false; + // Для GUI используем legacy формат &#RRGGBB который LegacyComponentSerializer понимает + boolean useLegacyForPreview = true; // Если нет кастомного префикса - используем LuckPerms префикс if ((prefixToUse == null || prefixToUse.isEmpty()) && module.getLuckPermsHook().isEnabled()) { String lpPrefix = module.getLuckPermsHook().getActivePrefix(player); if (lpPrefix != null && !lpPrefix.isEmpty()) { - // Для превью показываем LP префикс + // Конвертируем LP префикс в legacy формат + lpPrefix = convertToLegacy(lpPrefix); + // Если градиент на LP префикс включен - применяем градиент на всю строку if (colorsToUse != null && !colorsToUse.isEmpty() && module.getConfig().isGradientOnLuckPermsPrefix()) { - // Убираем цвета из LP префикса и применяем градиент на всё String cleanLpPrefix = stripColors(lpPrefix); String fullText = cleanLpPrefix + nick; return GradientUtil.applyGradient(fullText, colorsToUse, useLegacyForPreview); } else { - // LP префикс со своими цветами + градиентный ник if (colorsToUse != null && !colorsToUse.isEmpty()) { 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"); + } + /** * Убирает цветовые коды из строки */ diff --git a/src/main/java/com/loki/lochat/gradient/util/GradientUtil.java b/src/main/java/com/loki/lochat/gradient/util/GradientUtil.java index cf4eb3d..c7cfd2f 100644 --- a/src/main/java/com/loki/lochat/gradient/util/GradientUtil.java +++ b/src/main/java/com/loki/lochat/gradient/util/GradientUtil.java @@ -98,17 +98,59 @@ public final class GradientUtil { private static String formatHex(String hex, boolean useLegacyFormat) { hex = hex.replace("#", "").toLowerCase(); if (useLegacyFormat) { - // Формат §x§R§R§G§G§B§B для TAB и других плагинов - StringBuilder sb = new StringBuilder("§x"); - for (char c : hex.toCharArray()) { - sb.append("§").append(c); - } - return sb.toString(); + // Формат &#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"); + for (char c : hex.toCharArray()) { + sb.append("§").append(c); + } + return sb.toString(); + } + + /** + * Применяет градиент в формате §x§R§R§G§G§B§B для TAB плагина + */ + public static String applyGradientTabFormat(String text, List 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 colors, boolean gradientOnPrefix, boolean continuousGradient, String prefixFormat, boolean useLegacyFormat) { diff --git a/src/main/java/com/loki/lochat/integrations/PlaceholderAPIHook.java b/src/main/java/com/loki/lochat/integrations/PlaceholderAPIHook.java index dd8684f..09d9563 100644 --- a/src/main/java/com/loki/lochat/integrations/PlaceholderAPIHook.java +++ b/src/main/java/com/loki/lochat/integrations/PlaceholderAPIHook.java @@ -69,6 +69,7 @@ public class PlaceholderAPIHook extends PlaceholderExpansion { } // Gradient placeholders (совместимость с LoPreff) + // Используем TAB формат §x§R§R§G§G§B§B для совместимости с TAB плагином case "gradient_full", "full" -> { Player onlinePlayer = player.getPlayer(); 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 gradient.getFormattedName(onlinePlayer); + // Используем TAB формат для плейсхолдеров + yield gradient.getFormattedNameForTab(onlinePlayer); } case "gradient_name", "name" -> { Player onlinePlayer = player.getPlayer(); diff --git a/src/test/java/com/loki/lochat/gradient/util/GradientUtilTest.java b/src/test/java/com/loki/lochat/gradient/util/GradientUtilTest.java index e81b136..dbfc8bd 100644 --- a/src/test/java/com/loki/lochat/gradient/util/GradientUtilTest.java +++ b/src/test/java/com/loki/lochat/gradient/util/GradientUtilTest.java @@ -35,8 +35,8 @@ class GradientUtilTest { void applyGradient_singleColor_legacy() { String result = GradientUtil.applyGradient("Hi", List.of("#FF0000"), true); assertNotNull(result); - // Формат §x§f§f§0§0§0§0 - assertTrue(result.contains("§x§f§f§0§0§0§0")); + // Формат &#RRGGBB для LegacyComponentSerializer + assertTrue(result.contains("&#ff0000")); } @Test @@ -50,9 +50,9 @@ class GradientUtilTest { void applyGradient_twoColors() { String result = GradientUtil.applyGradient("AB", List.of("#FF0000", "#0000FF"), true); assertNotNull(result); - // Первый символ должен быть красным, последний синим - assertTrue(result.contains("§x§f§f§0§0§0§0")); // красный - assertTrue(result.contains("§x§0§0§0§0§f§f")); // синий + // Первый символ должен быть красным, последний синим (формат &#RRGGBB) + assertTrue(result.contains("&#ff0000")); // красный + assertTrue(result.contains("�ff")); // синий } @Test @@ -105,23 +105,24 @@ class GradientUtilTest { void buildDisplayName_noPrefix_withColors() { String result = GradientUtil.buildDisplayName(null, "AB", List.of("#FF0000", "#0000FF"), true, true, "[{prefix}] ", true); assertNotNull(result); - assertTrue(result.contains("§x")); + // Формат &#RRGGBB + assertTrue(result.contains("&#")); } @Test void buildDisplayName_withPrefix_withColors_continuous() { String result = GradientUtil.buildDisplayName("V", "AB", List.of("#FF0000", "#0000FF"), true, true, "[{prefix}] ", true); assertNotNull(result); - // Единый градиент на всё - assertTrue(result.contains("§x")); + // Единый градиент на всё (формат &#RRGGBB) + assertTrue(result.contains("&#")); } @Test void buildDisplayName_withPrefix_withColors_separate() { String result = GradientUtil.buildDisplayName("V", "AB", List.of("#FF0000", "#0000FF"), true, false, "[{prefix}] ", true); assertNotNull(result); - // Отдельные градиенты - assertTrue(result.contains("§x")); + // Отдельные градиенты (формат &#RRGGBB) + assertTrue(result.contains("&#")); } @Test @@ -131,4 +132,21 @@ class GradientUtilTest { // Префикс без градиента 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")); // синий + } }