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:
parent
036382b71a
commit
29ae373554
5 changed files with 127 additions and 26 deletions
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Убирает цветовые коды из строки
|
* Убирает цветовые коды из строки
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Убирает цветовые коды из строки
|
* Убирает цветовые коды из строки
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -98,17 +98,59 @@ 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
|
||||||
StringBuilder sb = new StringBuilder("§x");
|
return "&#" + hex;
|
||||||
for (char c : hex.toCharArray()) {
|
|
||||||
sb.append("§").append(c);
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
} else {
|
} else {
|
||||||
return "<#" + hex + ">";
|
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<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,
|
||||||
boolean gradientOnPrefix, boolean continuousGradient,
|
boolean gradientOnPrefix, boolean continuousGradient,
|
||||||
String prefixFormat, boolean useLegacyFormat) {
|
String prefixFormat, boolean useLegacyFormat) {
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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("�ff")); // синий
|
||||||
}
|
}
|
||||||
|
|
||||||
@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")); // синий
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue