Remove ChatColor system - unified all color handling in GradientModule + fix color formats

This commit is contained in:
loki5512344 2025-12-31 14:29:49 +01:00
parent dbb6a61351
commit d8721138d9
9 changed files with 16 additions and 441 deletions

View file

@ -26,11 +26,9 @@ public final class LoChat extends JavaPlugin {
private MentionManager mentionManager;
private SpyManager spyManager;
private EmojiManager emojiManager;
private ChatColorManager chatColorManager;
private CustomCommandManager customCommandManager;
private MuteManager muteManager;
private GradientModule gradientModule;
private com.loki.lochat.listeners.DisplayNameListener displayNameListener;
@Override
public void onEnable() {
@ -49,7 +47,6 @@ public final class LoChat extends JavaPlugin {
cooldownManager = new CooldownManager();
mentionManager = new MentionManager(this);
spyManager = new SpyManager(this);
chatColorManager = new ChatColorManager(this);
customCommandManager = new CustomCommandManager(this);
muteManager = new MuteManager(this);
chatManager = new ChatManager(this);
@ -66,8 +63,6 @@ public final class LoChat extends JavaPlugin {
// Регистрация листенеров
getServer().getPluginManager().registerEvents(new ChatListener(this), this);
displayNameListener = new com.loki.lochat.listeners.DisplayNameListener(this);
getServer().getPluginManager().registerEvents(displayNameListener, this);
// Запуск автосообщений
autoMessageManager.start();
@ -89,9 +84,6 @@ public final class LoChat extends JavaPlugin {
if (ignoreManager != null) {
ignoreManager.save();
}
if (chatColorManager != null) {
chatColorManager.saveData();
}
if (muteManager != null) {
muteManager.saveData();
}
@ -114,7 +106,6 @@ public final class LoChat extends JavaPlugin {
getCommand("clearchat").setExecutor(new ClearChatCommand(this));
getCommand("mute").setExecutor(new MuteCommand(this));
getCommand("unmute").setExecutor(new UnmuteCommand(this));
getCommand("chatcolor").setExecutor(new ChatColorCommand(this));
LoChatCommand loChatCommand = new LoChatCommand(this);
getCommand("lochat").setExecutor(loChatCommand);
@ -194,10 +185,6 @@ public final class LoChat extends JavaPlugin {
return gradientModule;
}
public ChatColorManager getChatColorManager() {
return chatColorManager;
}
public CustomCommandManager getCustomCommandManager() {
return customCommandManager;
}
@ -205,8 +192,4 @@ public final class LoChat extends JavaPlugin {
public MuteManager getMuteManager() {
return muteManager;
}
public com.loki.lochat.listeners.DisplayNameListener getDisplayNameListener() {
return displayNameListener;
}
}

View file

@ -1,94 +0,0 @@
package com.loki.lochat.commands;
import com.loki.lochat.LoChat;
import com.loki.lochat.utils.ChatFormatter;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
public class ChatColorCommand implements CommandExecutor, TabCompleter {
private final LoChat plugin;
private static final Pattern LEGACY_COLOR_PATTERN = Pattern.compile("^&([0-9a-fk-orA-FK-OR])$");
public ChatColorCommand(LoChat plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!(sender instanceof Player player)) {
sender.sendMessage("Только для игроков!");
return true;
}
if (!player.hasPermission("lochat.chatcolor")) {
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getNoPermission()));
return true;
}
if (args.length == 0) {
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getInvalidUsage("/chatcolor <цвет>")));
player.sendMessage(ChatFormatter.parse("&#808080Используйте Minecraft color codes: &a, &b, &c, &d, &e, &f, &0-&9, &k, &l, &m, &n, &o, &r"));
player.sendMessage(ChatFormatter.parse("&#808080Пример: /chatcolor &b"));
return true;
}
String color = args[0];
// Проверяем валидность цвета (формат &X где X - это цветовой код)
if (!isValidColor(color)) {
player.sendMessage(ChatFormatter.parse("&#FF0000Неверный формат цвета! Используйте формат &X, например: &b, &a, &c"));
return true;
}
// Сохраняем цвет чата игрока
plugin.getChatColorManager().setChatColor(player.getUniqueId(), color);
// Обновляем display name игрока
player.getScheduler().run(plugin, task -> {
if (plugin.getDisplayNameListener() != null) {
plugin.getDisplayNameListener().updatePlayerDisplayName(player);
}
}, null);
// Показываем пример с примененным цветом
String preview = ChatFormatter.convertAllColors(color + "Пример текста");
player.sendMessage(ChatFormatter.parse("&#00FF00Цвет чата установлен! Пример: " + preview));
return true;
}
private boolean isValidColor(String color) {
if (color == null || color.length() != 2) {
return false;
}
if (!color.startsWith("&")) {
return false;
}
char code = color.charAt(1);
return (code >= '0' && code <= '9') ||
(code >= 'a' && code <= 'f') ||
(code >= 'A' && code <= 'F') ||
code == 'k' || code == 'K' ||
code == 'l' || code == 'L' ||
code == 'm' || code == 'M' ||
code == 'n' || code == 'N' ||
code == 'o' || code == 'O' ||
code == 'r' || code == 'R';
}
@Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
// Убираем автодополнение, так как теперь пользователь сам вводит цвет
return new ArrayList<>();
}
}

View file

@ -20,8 +20,6 @@ public final class DisplayNameUtil {
.hexCharacter('#')
.hexColors()
.build();
private static final Pattern HEX_PATTERN = Pattern.compile("&#([0-9a-fA-F]{6})");
private DisplayNameUtil() {}
@ -52,8 +50,8 @@ public final class DisplayNameUtil {
cfg.isUseLegacyRgbFormat(),
cfg.isContinuousGradient()
);
// Конвертируем &#RRGGBB в §x§R§R§G§G§B§B для display name
displayName = convertToLegacyFormat(displayName);
// Для display name нужен §x формат, конвертируем MiniMessage в legacy
displayName = convertMiniMessageToLegacy(displayName);
var component = SERIALIZER.deserialize(displayName);
player.displayName(component);
player.playerListName(component);
@ -72,20 +70,22 @@ public final class DisplayNameUtil {
cfg.isUseLegacyRgbFormat()
);
// Конвертируем &#RRGGBB в §x§R§R§G§G§B§B для display name
displayName = convertToLegacyFormat(displayName);
// Для display name нужен §x формат, конвертируем MiniMessage в legacy
displayName = convertMiniMessageToLegacy(displayName);
var component = SERIALIZER.deserialize(displayName);
player.displayName(component);
player.playerListName(component);
}
/**
* Конвертирует &#RRGGBB формат в §x§R§R§G§G§B§B для display name
* Конвертирует MiniMessage <#RRGGBB> формат в §x§R§R§G§G§B§B для display name
*/
private static String convertToLegacyFormat(String text) {
private static String convertMiniMessageToLegacy(String text) {
if (text == null) return "";
Matcher matcher = HEX_PATTERN.matcher(text);
// Конвертируем <#RRGGBB> в §x§R§R§G§G§B§B
Pattern pattern = Pattern.compile("<#([0-9a-fA-F]{6})>");
Matcher matcher = pattern.matcher(text);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {

View file

@ -150,18 +150,15 @@ public class TextDisplayManager {
boolean useLegacyFormat,
boolean continuousGradient) {
if (colors == null || colors.isEmpty()) {
return convertToMiniMessageFormat(lpPrefix + nick);
return lpPrefix + nick;
}
if (continuousGradient) {
String cleanPrefix = stripColors(lpPrefix);
String fullText = cleanPrefix + nick;
String result = GradientUtil.applyGradient(fullText, colors, false); // MiniMessage формат
return convertToMiniMessageFormat(result);
return GradientUtil.applyGradient(fullText, colors, false); // Всегда MiniMessage формат для TextDisplay
} else {
String result = convertToMiniMessageFormat(lpPrefix) +
convertToMiniMessageFormat(GradientUtil.applyGradient(nick, colors, false));
return result;
return lpPrefix + GradientUtil.applyGradient(nick, colors, false); // Всегда MiniMessage формат для TextDisplay
}
}
@ -174,25 +171,10 @@ public class TextDisplayManager {
}
/**
* Конвертирует &#RRGGBB в <#RRGGBB> для MiniMessage
* Возвращает текст как есть, так как градиенты уже в MiniMessage формате
*/
private String convertToMiniMessageFormat(String text) {
if (text == null) return "";
// Конвертируем &#RRGGBB в <#RRGGBB>
text = text.replaceAll("&#([0-9a-fA-F]{6})", "<#$1>");
// Конвертируем §x§R§R§G§G§B§B в <#RRGGBB> (используем Pattern и Matcher)
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("§x(§[0-9a-fA-F]){6}");
java.util.regex.Matcher matcher = pattern.matcher(text);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
String hex = matcher.group().replaceAll("§", "").substring(1); // Убираем все § и x
matcher.appendReplacement(sb, "<#" + hex + ">");
}
matcher.appendTail(sb);
text = sb.toString();
// Конвертируем обычные цветовые коды §a в <green> и т.д.
text = text.replaceAll("§([0-9a-fk-or])", "<$1>");
return text;
}

View file

@ -1,139 +0,0 @@
package com.loki.lochat.listeners;
import com.loki.lochat.LoChat;
import com.loki.lochat.managers.ChatColorManager;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
/**
* Слушатель для обновления display name игроков с префиксом и цветом ника
*/
public class DisplayNameListener implements Listener {
private final LoChat plugin;
private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage();
public DisplayNameListener(LoChat plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
// Обновляем display name через RegionScheduler для Folia
player.getScheduler().run(plugin, task -> {
updatePlayerDisplayName(player);
}, null);
}
/**
* Обновляет display name игрока с префиксом и цветом ника
*/
public void updatePlayerDisplayName(Player player) {
// Если градиентный модуль включен и обновляет display name, не дублируем
if (plugin.getGradientModule() != null && plugin.getGradientModule().isEnabled()) {
if (plugin.getGradientModule().getConfig().isUpdateDisplayName()) {
// Градиентный модуль сам обновляет display name
return;
}
}
String displayName = buildDisplayName(player);
Component component = MINI_MESSAGE.deserialize(displayName);
player.displayName(component);
}
/**
* Строит display name с префиксом и цветом ника
*/
private String buildDisplayName(Player player) {
StringBuilder result = new StringBuilder();
// Получаем префикс из градиентного модуля (если есть)
String prefix = null;
if (plugin.getGradientModule() != null && plugin.getGradientModule().isEnabled()) {
var data = plugin.getGradientModule().getDataManager().getPlayerData(player.getUniqueId());
if (data != null && data.isPrefixEnabled() && data.hasPrefix()) {
prefix = plugin.getGradientModule().getConfig().getPrefixFormat()
.replace("{prefix}", data.getPrefix());
}
}
// Добавляем префикс если есть
if (prefix != null && !prefix.isEmpty()) {
result.append(prefix);
}
// Получаем цвет чата
ChatColorManager colorManager = plugin.getChatColorManager();
String chatColor = colorManager.getChatColor(player.getUniqueId());
// Получаем имя игрока
String playerName = player.getName();
// Применяем цвет к нику
if (chatColor != null) {
// Если цвет в формате &X, конвертируем его в MiniMessage
if (chatColor.startsWith("&") && chatColor.length() == 2) {
String miniMessageColor = convertLegacyColorToMiniMessage(chatColor);
if (!miniMessageColor.isEmpty()) {
String tagName = miniMessageColor.substring(1, miniMessageColor.length() - 1);
result.append(miniMessageColor).append(playerName).append("</").append(tagName).append(">");
} else {
result.append(playerName);
}
} else {
// Если уже в формате MiniMessage
result.append("<").append(chatColor).append(">").append(playerName).append("</").append(chatColor).append(">");
}
} else {
// Нет цвета, просто имя
result.append(playerName);
}
return result.toString();
}
/**
* Конвертирует &X код в MiniMessage формат
*/
private String convertLegacyColorToMiniMessage(String legacyColor) {
if (legacyColor == null || legacyColor.length() != 2 || !legacyColor.startsWith("&")) {
return "";
}
char code = legacyColor.charAt(1);
return switch (Character.toLowerCase(code)) {
case '0' -> "<black>";
case '1' -> "<dark_blue>";
case '2' -> "<dark_green>";
case '3' -> "<dark_aqua>";
case '4' -> "<dark_red>";
case '5' -> "<dark_purple>";
case '6' -> "<gold>";
case '7' -> "<gray>";
case '8' -> "<dark_gray>";
case '9' -> "<blue>";
case 'a' -> "<green>";
case 'b' -> "<aqua>";
case 'c' -> "<red>";
case 'd' -> "<light_purple>";
case 'e' -> "<yellow>";
case 'f' -> "<white>";
case 'k' -> "<obfuscated>";
case 'l' -> "<bold>";
case 'm' -> "<strikethrough>";
case 'n' -> "<underlined>";
case 'o' -> "<italic>";
case 'r' -> "<reset>";
default -> "";
};
}
}

View file

@ -1,148 +0,0 @@
package com.loki.lochat.managers;
import com.loki.lochat.LoChat;
import com.loki.lochat.gradient.util.FoliaUtil;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class ChatColorManager {
private final LoChat plugin;
private final Map<UUID, String> chatColors;
private File dataFile;
private FileConfiguration dataConfig;
public ChatColorManager(LoChat plugin) {
this.plugin = plugin;
this.chatColors = new HashMap<>();
loadData();
}
private void loadData() {
dataFile = new File(plugin.getDataFolder(), "chatcolors.yml");
if (!dataFile.exists()) {
try {
dataFile.createNewFile();
} catch (IOException e) {
plugin.getLogger().severe("Не удалось создать файл chatcolors.yml: " + e.getMessage());
}
}
dataConfig = YamlConfiguration.loadConfiguration(dataFile);
// Загружаем цвета из файла
for (String key : dataConfig.getKeys(false)) {
try {
UUID uuid = UUID.fromString(key);
String color = dataConfig.getString(key);
if (color != null) {
chatColors.put(uuid, color);
}
} catch (IllegalArgumentException ignored) {
// Неверный UUID, пропускаем
}
}
}
public void saveData() {
for (Map.Entry<UUID, String> entry : chatColors.entrySet()) {
dataConfig.set(entry.getKey().toString(), entry.getValue());
}
try {
dataConfig.save(dataFile);
} catch (IOException e) {
plugin.getLogger().severe("Не удалось сохранить chatcolors.yml: " + e.getMessage());
}
}
/**
* Асинхронное сохранение данных (для Folia совместимости)
*/
private void saveDataAsync() {
FoliaUtil.runAsync(plugin, this::saveData);
}
public void setChatColor(UUID playerId, String color) {
chatColors.put(playerId, color);
saveDataAsync();
}
public String getChatColor(UUID playerId) {
return chatColors.get(playerId);
}
public boolean hasChatColor(UUID playerId) {
return chatColors.containsKey(playerId);
}
public void removeChatColor(UUID playerId) {
chatColors.remove(playerId);
dataConfig.set(playerId.toString(), null);
saveDataAsync();
}
/**
* Применяет цвет чата к сообщению
* Поддерживает формат &X (Minecraft color codes)
*/
public String applyChatColor(UUID playerId, String message) {
String color = getChatColor(playerId);
if (color != null) {
// Если цвет в формате &X, конвертируем его в MiniMessage
if (color.startsWith("&") && color.length() == 2) {
String miniMessageColor = convertLegacyColorToMiniMessage(color);
if (!miniMessageColor.isEmpty()) {
// Извлекаем имя тега из <tag> для закрывающего тега
String tagName = miniMessageColor.substring(1, miniMessageColor.length() - 1);
return miniMessageColor + message + "</" + tagName + ">";
}
}
// Если уже в формате MiniMessage, используем как есть
return "<" + color + ">" + message + "</" + color + ">";
}
return message;
}
/**
* Конвертирует &X код в MiniMessage формат
*/
private String convertLegacyColorToMiniMessage(String legacyColor) {
if (legacyColor == null || legacyColor.length() != 2 || !legacyColor.startsWith("&")) {
return "";
}
char code = legacyColor.charAt(1);
return switch (Character.toLowerCase(code)) {
case '0' -> "<black>";
case '1' -> "<dark_blue>";
case '2' -> "<dark_green>";
case '3' -> "<dark_aqua>";
case '4' -> "<dark_red>";
case '5' -> "<dark_purple>";
case '6' -> "<gold>";
case '7' -> "<gray>";
case '8' -> "<dark_gray>";
case '9' -> "<blue>";
case 'a' -> "<green>";
case 'b' -> "<aqua>";
case 'c' -> "<red>";
case 'd' -> "<light_purple>";
case 'e' -> "<yellow>";
case 'f' -> "<white>";
case 'k' -> "<obfuscated>";
case 'l' -> "<bold>";
case 'm' -> "<strikethrough>";
case 'n' -> "<underlined>";
case 'o' -> "<italic>";
case 'r' -> "<reset>";
default -> "";
};
}
}

View file

@ -41,12 +41,11 @@ public final class ChatFormatter {
/* ===================== COLORS ===================== */
/**
* Конвертирует &#RRGGBB формат в MiniMessage <#RRGGBB> формат
* Возвращает строку как есть, так как теперь градиенты генерируются в MiniMessage формате
*/
public static String convertAllColors(String message) {
if (message == null) return "";
// Конвертируем &#RRGGBB в <#RRGGBB> для MiniMessage
return message.replaceAll("&#([0-9a-fA-F]{6})", "<#$1>");
return message;
}
/* ===================== EMOJIS ===================== */