Войти или зарегистрироваться
  • Вы находитесь в сообществе Rubukkit. Мы - администраторы серверов Minecraft, разрабатываем собственные плагины и переводим на различные языки плагины наших коллег из других стран.

Помогите Поддержка текста с градиентом

Aviloo

Активный участник
Пользователь
Баллы
61
Имя в Minecraft
Aviloo
Всем привет. У меня есть небольшой плагин на скорборд. Проблема собственно состоит в том, что если ,например, у меня на сервере есть префиксы с использованием градиента , то в скорборде текст отображается, как каша из символов. Хотел узнать, как можно реализовать отображение текста с градиентом.
 
как каша из символов
В чём конкретно проблема? Цветовые коды отображаются текстом? Хоть бы скрин показал.

И какое ядро и версия?

На Paper и его форках начиная с 1.16 (возможно 1.16.5) есть поддержка Adventure API, для получения градиента можно парсить строку в формате MiniMessage. Тут всё просто:
PHP:
@Nonnull
public static Component createRgbGradient(@Nonnull String text, @Nonnull TextColor start, @Nonnull TextColor end) {
    return MiniMessage.miniMessage().deserialize(
        "<gradient:" + start.asHexString() + ":" + end.asHexString() + ">"
            + text
            + "</gradient>"
    );
}

@Nonnull
public static String create16ColorsGradient(@Nonnull String text, @Nonnull TextColor start, @Nonnull TextColor end) {
    return LegacyComponentSerializer.legacySection().serialize(createRgbGradient(text, start, end));
}
Возможно когда-нибудь появится нормальный метод в апи для создания RGB-градиентов, но пока только через MiniMessage.

На Spigot или на старых версиях Paper (до 1.15 включительно) нет возможности задавать параметры Objective, Team и Entry компонентами из Bungee или Adventure, поэтому придётся обычной строкой:
PHP:
@Nonnull
public static String createRgbGradient(@Nonnull String text, @Nonnull ChatColor start, @Nonnull ChatColor end) {
    return createGradient(text, start, end, true);
}

@Nonnull
public static String create16ColorsGradient(@Nonnull String text, @Nonnull ChatColor start, @Nonnull ChatColor end) {
    return createGradient(text, start, end, false);
}

@Nonnull
private static String createGradient(@Nonnull String text, @Nonnull ChatColor start, @Nonnull ChatColor end, boolean useRGB) {
    char[] chars = text.toCharArray();
    int length = chars.length;

    StringBuilder result = new StringBuilder();
    for (int i = 0; i < length; i++) {
        ChatColor color = getGradientColor(start, end, ((double) i) / length);
        if (!useRGB) {
            color = nearestNamedTo(color);
        }
        result.append(color).append(chars[i]);
    }

    return result.toString();
}

@Nonnull
private static ChatColor getGradientColor(@Nonnull ChatColor start, @Nonnull ChatColor end, double percent) {
    Color s = start.getColor();
    Color e = end.getColor();
    return ChatColor.of(new Color(
        (int) (s.getRed() + ((e.getRed() - s.getRed()) * percent)),
        (int) (s.getGreen() + ((e.getGreen() - s.getGreen()) * percent)),
        (int) (s.getBlue() + ((e.getBlue() - s.getBlue()) * percent))
    ));
}

private static final ChatColor[] NAMED_COLORS = new ChatColor[]{
    ChatColor.BLACK, ChatColor.DARK_BLUE, ChatColor.DARK_GREEN, ChatColor.DARK_AQUA,
    ChatColor.DARK_RED, ChatColor.DARK_PURPLE, ChatColor.GOLD, ChatColor.GRAY,
    ChatColor.DARK_GRAY, ChatColor.BLUE, ChatColor.GREEN, ChatColor.AQUA,
    ChatColor.RED, ChatColor.LIGHT_PURPLE, ChatColor.YELLOW, ChatColor.WHITE
};

@Nonnull
private static ChatColor nearestNamedTo(@Nonnull ChatColor any) {
    float matchedDistance = Float.MAX_VALUE;
    ChatColor match = NAMED_COLORS[0];
    int i = 0;

    for (int length = NAMED_COLORS.length; i < length; ++i) {
        ChatColor potential = NAMED_COLORS[i];

        float distance = distance(any.getColor(), potential.getColor());
        if (distance < matchedDistance) {
            match = potential;
            matchedDistance = distance;
        }

        if (distance == 0.0F) {
            break;
        }
    }

    return match;
}

private static float distance(@Nonnull Color first, @Nonnull Color second) {
    float[] selfHVS = Color.RGBtoHSB(first.getRed(), first.getGreen(), first.getBlue(), null);
    float[] otherHVS = Color.RGBtoHSB(second.getRed(), second.getGreen(), second.getBlue(), null);
    float hueDistance = 3.0F * Math.min(Math.abs(selfHVS[0] - otherHVS[0]), 1.0F - Math.abs(selfHVS[0] - otherHVS[0]));
    float saturationDiff = selfHVS[1] - otherHVS[1];
    float valueDiff = selfHVS[2] - otherHVS[2];
    return (hueDistance * hueDistance) + (saturationDiff * saturationDiff) + (valueDiff * valueDiff);
}
Важно: Убедись, что в импортах net.md_5.bungee.api.ChatColor, а не org.bukkit.ChatColor. Баккитовский вообще не поддерживает RGB.


Данный код отлично подходит для большинства задач: для отправки сообщений в чат, создания названий предметов и задания названий скорбордам, а также создания префиксов/суффиксов команд.

Однако независимо от версии игры и ядра score (entry команд) не поддерживают RGB.
Результат:
upload_2023-10-14_23-48-48.png
PHP:
public static void test(@Nonnull Player player) {
    ScoreboardManager manager = player.getServer().getScoreboardManager();
    Scoreboard board = manager.getNewScoreboard();

    Objective objective = board.registerNewObjective(randomString(), Criteria.DUMMY, gradient("OBJ_NAME"));
    objective.setDisplaySlot(DisplaySlot.SIDEBAR);

    Team team = board.registerNewTeam(randomString());
    team.prefix(gradient("PREFIX"));
    team.displayName(Component.text(randomString()));
    team.suffix(gradient("SUFFIX"));

    String score = LegacyComponentSerializer.legacySection().serialize(gradient("SCORE"));
    team.addEntry(score);
    objective.getScore(score).setScore(0);

    player.setScoreboard(board);
}

@Nonnull
private static Component gradient(@Nonnull String text) {
    return createRgbGradient(text, NamedTextColor.WHITE, NamedTextColor.BLACK);
}

@Nonnull
private static String randomString() {
    return UUID.randomUUID().toString();
}
Результат:
upload_2023-10-12_12-24-54.png
PHP:
public static void test(@Nonnull Player player) {
    ScoreboardManager manager = player.getServer().getScoreboardManager();
    Scoreboard board = manager.getNewScoreboard();

    Objective objective = board.registerNewObjective(randomString(), "dummy");
    objective.setDisplaySlot(DisplaySlot.SIDEBAR);
    objective.setDisplayName(gradient("OBJ_NAME"));

    Team team = board.registerNewTeam(randomString());
    team.setPrefix(gradient("PREFIX"));
    team.setDisplayName(randomString());
    team.setSuffix(gradient("SUFFIX"));

    String score = gradient("SCORE");
    team.addEntry(score);
    objective.getScore(score).setScore(0);

    player.setScoreboard(board);
}

@Nonnull
private static String gradient(@Nonnull String text) {
    return createRgbGradient(text, ChatColor.WHITE, ChatColor.BLACK);
}

@Nonnull
private static String randomString() {
    return UUID.randomUUID().toString();
}

Решать эту проблему можно двумя путями:

1) Отказаться от RGB и использовать лишь 16 цветов при помощи цветовых кодов (параграфов).
Для этого и нужен метод create16ColorsGradient(), который в коде выше.

2) Скрыть эти entry вовсе и использовать исключительно префиксы и суффиксы команд.
В качестве названия entry в этом случае можно применять уникальную комбинацию цветовых кодов примерно таким образом:
PHP:
private int lastEntryNumber = 0;

@Nonnull
private String randomEntry() {
    StringBuilder entry = new StringBuilder();
    for (char ch : String.format("%04X", this.lastEntryNumber++).toLowerCase().toCharArray()) {
        entry.append('§').append(ch);
    }
    return entry.toString();
}
Entry при этом будут разные, но в игре отображаться не будут.

P.S. По логике ты можешь использовать AdventureAPI и на спиготе 1.16+, но в этом случае не забудь добавить зависимость в джарник плагина или указать в libraries в plugin.yml. В целом с Adventure работать удобней, чем со строками или компонентами Bungee
 
Последнее редактирование:
В чём конкретно проблема? Цветовые коды отображаются текстом? Хоть бы скрин показал.

И какое ядро и версия?

На Paper и его форках начиная с 1.16 (возможно 1.16.5) есть поддержка Adventure API, для получения градиента можно парсить строку в формате MiniMessage. Тут всё просто:
PHP:
@Nonnull
public static Component createGradient(@Nonnull String text, @Nonnull TextColor start, @Nonnull TextColor end) {
    return MiniMessage.miniMessage().deserialize(
        "<gradient:" + start.asHexString() + ":" + end.asHexString() + ">"
            + text
            + "</gradient>"
    );
}
Возможно когда-нибудь появится нормальный метод в апи, но пока так.

На Spigot или более старых версиях нет возможности задавать параметры Objective, Team и Entry компонентами из md_5 или adventure, поэтому придётся обычной строкой. Используй это со значением useRGB = true:
PHP:
@Nonnull
public static String createGradient(@Nonnull String text, @Nonnull ChatColor start, @Nonnull ChatColor end, boolean useRGB) {
    char[] chars = text.toCharArray();
    int length = chars.length;

    StringBuilder result = new StringBuilder();
    for (int i = 0; i < length; i++) {
        ChatColor color = getGradientColor(start, end, ((double) i) / length);
        if (!useRGB) {
            color = nearestNamedTo(color);
        }
        result.append(color).append(chars[i]);
    }

    return result.toString();
}

@Nonnull
private static ChatColor getGradientColor(@Nonnull ChatColor start, @Nonnull ChatColor end, double percent) {
    Color s = start.getColor();
    Color e = end.getColor();
    return ChatColor.of(new Color(
        (int) (s.getRed() + ((e.getRed() - s.getRed()) * percent)),
        (int) (s.getGreen() + ((e.getGreen() - s.getGreen()) * percent)),
        (int) (s.getBlue() + ((e.getBlue() - s.getBlue()) * percent))
    ));
}

private static final ChatColor[] NAMED_COLORS = new ChatColor[]{
    ChatColor.BLACK, ChatColor.DARK_BLUE, ChatColor.DARK_GREEN, ChatColor.DARK_AQUA,
    ChatColor.DARK_RED, ChatColor.DARK_PURPLE, ChatColor.GOLD, ChatColor.GRAY,
    ChatColor.DARK_GRAY, ChatColor.BLUE, ChatColor.GREEN, ChatColor.AQUA,
    ChatColor.RED, ChatColor.LIGHT_PURPLE, ChatColor.YELLOW, ChatColor.WHITE
};

@Nonnull
private static ChatColor nearestNamedTo(@Nonnull ChatColor any) {
    float matchedDistance = Float.MAX_VALUE;
    ChatColor match = NAMED_COLORS[0];
    int i = 0;

    for (int length = NAMED_COLORS.length; i < length; ++i) {
        ChatColor potential = NAMED_COLORS[i];

        float distance = distance(any.getColor(), potential.getColor());
        if (distance < matchedDistance) {
            match = potential;
            matchedDistance = distance;
        }

        if (distance == 0.0F) {
            break;
        }
    }

    return match;
}

private static float distance(@Nonnull Color first, @Nonnull Color second) {
    float[] selfHVS = Color.RGBtoHSB(first.getRed(), first.getGreen(), first.getBlue(), null);
    float[] otherHVS = Color.RGBtoHSB(second.getRed(), second.getGreen(), second.getBlue(), null);
    float hueDistance = 3.0F * Math.min(Math.abs(selfHVS[0] - otherHVS[0]), 1.0F - Math.abs(selfHVS[0] - otherHVS[0]));
    float saturationDiff = selfHVS[1] - otherHVS[1];
    float valueDiff = selfHVS[2] - otherHVS[2];
    return (hueDistance * hueDistance) + (saturationDiff * saturationDiff) + (valueDiff * valueDiff);
}
Важно: Убедись, что в импортах net.md_5.bungee.api.ChatColor, а не
org.bukkit.ChatColor. Баккитовский вообще не поддерживает RGB.

Данный метод подходит для большинства задач: например для отправки сообщений в чат, создания названий предметов и задания названий скорбордам, а также префиксам/суффиксам команд. А вот для названий entry в скорбордах, к сожалению, не подходит. Эти записи поддерживают только 16 цветов:
Посмотреть вложение 66010
PHP:
ScoreboardManager manager = plugin.getServer().getScoreboardManager();
Scoreboard board = manager.getNewScoreboard();

Objective objective = board.registerNewObjective(randomString(), "dummy");
objective.setDisplaySlot(DisplaySlot.SIDEBAR);
objective.setDisplayName(gradient("OBJ_NAME"));

Team team = board.registerNewTeam(randomString());
team.setPrefix(gradient("PREFIX"));
team.setDisplayName(randomString());
team.setSuffix(gradient("SUFFIX"));

String score = gradient("SCORE");
team.addEntry(score);
objective.getScore(score).setScore(0);

player.setScoreboard(board);

Поэтому ты можешь либо смириться с 16-ю цветами (useRGB = false при создании названий entry).

Либо же отказаться от отображения текста в названиях entry и использовать исключительно префиксы/суффиксы команд. А в качестве названия entry в этом случае можно применять уникальную комбинацию цветовых кодов примерно таким образом:
PHP:
    private int lastEntryNumber = 0;

    @Nonnull
    private String randomEntry() {
        StringBuilder entry = new StringBuilder();
        for (char ch : String.format("%04X", this.lastEntryNumber++).toLowerCase().toCharArray()) {
            entry.append('§').append(ch);
        }
        return entry.toString();
    }
Entry при этом будут разные, но в игре отображаться не будут.

Либо же попробуй подключить Adventure API в качестве зависимости - возможно там эта проблема решена. Но не забудь добавить классы Adventure в джарник. Только придется ещё и скорборды перевести на Adventure. Я сильно сомневаюсь, что это сработает, однако в целом с Adventure работать удобней, чем со строками или компонентами bungee
Привет. Спасибо за ответ. Скрин : upload_2023-10-12_22-49-26.png

Версия: 1.19+
 
Привет. Спасибо за ответ. Скрин : Посмотреть вложение 66011

Версия: 1.19+
Ну, если Paper или форки - используй компоненты Adventure. Если Spigot, то строки обычные.
Единственное, что score (entry команд) даже на 1.19 являются обычной строкой, которая не поддерживает RGB, поэтому я бы в любом случае рекомендовал скрывать их и использовать для отображения текста только префиксы и суффиксы команд.
Чуть подправил своё предыдущее сообщение и расписал там подробнее, как это сделать
 
Последнее редактирование:

Поделиться этой страницей

Назад
Верх