Fix Folia chat colors - use Component API properly - ChatListener: use MessageConfig.getComponent() for error messages - ChatManager: build format string then parse with MiniMessage.deserialize() - ChatFormatter.convertAllColors: made idempotent (skip if already has <color:#>) - Removed old formatGlobalMessage/formatLocalMessage methods - All chat messages now properly rendered with colors

This commit is contained in:
loki5512344 2025-12-30 22:46:58 +01:00
parent 2bc9cfd761
commit 30d2c97e4f
5 changed files with 149 additions and 36 deletions

View file

@ -2,6 +2,8 @@ package com.loki.lochat.commands;
import com.loki.lochat.LoChat; import com.loki.lochat.LoChat;
import com.loki.lochat.utils.ChatFormatter; import com.loki.lochat.utils.ChatFormatter;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -11,6 +13,7 @@ import org.jetbrains.annotations.NotNull;
public class GlobalChatCommand implements CommandExecutor { public class GlobalChatCommand implements CommandExecutor {
private final LoChat plugin; private final LoChat plugin;
private static final MiniMessage MM = MiniMessage.miniMessage();
public GlobalChatCommand(LoChat plugin) { public GlobalChatCommand(LoChat plugin) {
this.plugin = plugin; this.plugin = plugin;
@ -42,7 +45,11 @@ public class GlobalChatCommand implements CommandExecutor {
} }
String message = String.join(" ", args); String message = String.join(" ", args);
plugin.getChatManager().sendGlobalMessage(player, message);
// Конвертируем String в Component для новой сигнатуры ChatManager
Component messageComponent = MM.deserialize(message);
plugin.getChatManager().sendGlobalMessage(player, messageComponent);
return true; return true;
} }
} }

View file

@ -1,7 +1,8 @@
package com.loki.lochat.commands; package com.loki.lochat.commands;
import com.loki.lochat.LoChat; import com.loki.lochat.LoChat;
import com.loki.lochat.utils.ChatFormatter; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -15,6 +16,7 @@ import java.util.List;
public class LoChatCommand implements CommandExecutor, TabCompleter { public class LoChatCommand implements CommandExecutor, TabCompleter {
private final LoChat plugin; private final LoChat plugin;
private static final MiniMessage MM = MiniMessage.miniMessage();
public LoChatCommand(LoChat plugin) { public LoChatCommand(LoChat plugin) {
this.plugin = plugin; this.plugin = plugin;
@ -24,40 +26,40 @@ public class LoChatCommand implements CommandExecutor, TabCompleter {
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command,
@NotNull String label, @NotNull String[] args) { @NotNull String label, @NotNull String[] args) {
if (!sender.hasPermission("lochat.admin")) { if (!sender.hasPermission("lochat.admin")) {
sender.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getNoPermission())); sender.sendMessage(MM.deserialize(plugin.getMessageConfig().getNoPermission()));
return true; return true;
} }
if (args.length == 0) { if (args.length == 0) {
sender.sendMessage(ChatFormatter.parse("&#FFD700LoChat v" + plugin.getDescription().getVersion())); sender.sendMessage(MM.deserialize("&#FFD700LoChat v" + plugin.getDescription().getVersion()));
sender.sendMessage(ChatFormatter.parse("&#808080Команды:")); sender.sendMessage(MM.deserialize("&#808080Команды:"));
sender.sendMessage(ChatFormatter.parse("&#FFFF00/lochat reload - Перезагрузить конфиги")); sender.sendMessage(MM.deserialize("&#FFFF00/lochat reload - Перезагрузить конфиги"));
sender.sendMessage(ChatFormatter.parse("&#FFFF00/lochat commands - Список кастомных команд")); sender.sendMessage(MM.deserialize("&#FFFF00/lochat commands - Список кастомных команд"));
sender.sendMessage(ChatFormatter.parse("&#FFFF00/lochat commands reload - Перезагрузить кастомные команды")); sender.sendMessage(MM.deserialize("&#FFFF00/lochat commands reload - Перезагрузить кастомные команды"));
return true; return true;
} }
switch (args[0].toLowerCase()) { switch (args[0].toLowerCase()) {
case "reload" -> { case "reload" -> {
plugin.reload(); plugin.reload();
sender.sendMessage(ChatFormatter.parse("&#00FF00Конфигурация перезагружена!")); sender.sendMessage(MM.deserialize("&#00FF00Конфигурация перезагружена!"));
} }
case "commands" -> { case "commands" -> {
if (args.length > 1 && args[1].equalsIgnoreCase("reload")) { if (args.length > 1 && args[1].equalsIgnoreCase("reload")) {
plugin.getCustomCommandManager().reload(); plugin.getCustomCommandManager().reload();
sender.sendMessage(ChatFormatter.parse("&#00FF00Кастомные команды перезагружены!")); sender.sendMessage(MM.deserialize("&#00FF00Кастомные команды перезагружены!"));
} else { } else {
var commands = plugin.getCustomCommandManager().getCommands(); var commands = plugin.getCustomCommandManager().getCommands();
sender.sendMessage(ChatFormatter.parse("&#FFD700Кастомные команды (" + commands.size() + "):")); sender.sendMessage(MM.deserialize("&#FFD700Кастомные команды (" + commands.size() + "):"));
for (var cmd : commands.values()) { for (var cmd : commands.values()) {
sender.sendMessage(ChatFormatter.parse("&e/" + cmd.name + " &#808080(" + cmd.type + ")")); sender.sendMessage(MM.deserialize("&e/" + cmd.name + " &#808080(" + cmd.type + ")"));
if (!cmd.aliases.isEmpty()) { if (!cmd.aliases.isEmpty()) {
sender.sendMessage(ChatFormatter.parse("&#808080 Алиасы: " + String.join(", ", cmd.aliases))); sender.sendMessage(MM.deserialize("&#808080 Алиасы: " + String.join(", ", cmd.aliases)));
} }
} }
} }
} }
default -> sender.sendMessage(ChatFormatter.parse("&#FF0000Неизвестная команда. Используйте /lochat для помощи.")); default -> sender.sendMessage(MM.deserialize("&#FF0000Неизвестная команда. Используйте /lochat для помощи."));
} }
return true; return true;
@ -94,4 +96,4 @@ public class LoChatCommand implements CommandExecutor, TabCompleter {
return new ArrayList<>(); return new ArrayList<>();
} }
} }

View file

@ -2,6 +2,8 @@ package com.loki.lochat.commands;
import com.loki.lochat.LoChat; import com.loki.lochat.LoChat;
import com.loki.lochat.utils.ChatFormatter; import com.loki.lochat.utils.ChatFormatter;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -11,6 +13,7 @@ import org.jetbrains.annotations.NotNull;
public class LocalChatCommand implements CommandExecutor { public class LocalChatCommand implements CommandExecutor {
private final LoChat plugin; private final LoChat plugin;
private static final MiniMessage MM = MiniMessage.miniMessage();
public LocalChatCommand(LoChat plugin) { public LocalChatCommand(LoChat plugin) {
this.plugin = plugin; this.plugin = plugin;
@ -29,7 +32,11 @@ public class LocalChatCommand implements CommandExecutor {
} }
String message = String.join(" ", args); String message = String.join(" ", args);
plugin.getChatManager().sendLocalMessage(player, message);
// Конвертируем String в Component для новой сигнатуры ChatManager
Component messageComponent = MM.deserialize(message);
plugin.getChatManager().sendLocalMessage(player, messageComponent);
return true; return true;
} }
} }

View file

@ -5,6 +5,7 @@ import com.loki.lochat.utils.ChatFormatter;
import com.loki.lochat.utils.DistanceUtil; import com.loki.lochat.utils.DistanceUtil;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -18,37 +19,37 @@ public class ChatManager {
private final LoChat plugin; private final LoChat plugin;
private final Set<UUID> globalChatDisabled = ConcurrentHashMap.newKeySet(); private final Set<UUID> globalChatDisabled = ConcurrentHashMap.newKeySet();
private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage(); private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage();
private static final PlainTextComponentSerializer PLAIN = PlainTextComponentSerializer.plainText();
public ChatManager(LoChat plugin) { public ChatManager(LoChat plugin) {
this.plugin = plugin; this.plugin = plugin;
} }
public void sendGlobalMessage(Player sender, String message) { public void sendGlobalMessage(Player sender, Component message) {
if (!plugin.getConfigManager().isGlobalEnabled()) { if (!plugin.getConfigManager().isGlobalEnabled()) {
sender.sendMessage(plugin.getMessageConfig().getComponent("global.disabled")); sender.sendMessage(plugin.getMessageConfig().getComponent("global.disabled"));
return; return;
} }
// Конвертируем Component в String для обработки упоминаний
String messageText = PLAIN.serialize(message);
// Обработка упоминаний @ник // Обработка упоминаний @ник
Set<Player> mentionedPlayers = new HashSet<>(); Set<Player> mentionedPlayers = new HashSet<>();
String processedMessage = plugin.getMentionManager().processMentions(message, mentionedPlayers); String processedMessage = plugin.getMentionManager().processMentions(messageText, mentionedPlayers);
String format = plugin.getConfigManager().getGlobalFormat(); String format = plugin.getConfigManager().getGlobalFormat();
String prefix = plugin.getConfigManager().getGlobalPrefix(); String prefix = plugin.getConfigManager().getGlobalPrefix();
boolean hasColor = sender.hasPermission("chat.global.color");
// Получаем отображаемое имя игрока (с градиентом если есть) // Получаем отображаемое имя игрока (с градиентом если есть)
String playerDisplay = getPlayerDisplay(sender); String playerDisplay = getPlayerDisplay(sender);
// Обрабатываем сообщение игрока (смайлики, цвета если есть право)
String finalMessage = ChatFormatter.processPlayerMessage(processedMessage, sender, hasColor);
// Отправка каждому игроку персонализированное сообщение // Отправка каждому игроку персонализированное сообщение
for (Player player : Bukkit.getOnlinePlayers()) { for (Player player : Bukkit.getOnlinePlayers()) {
if (isGlobalChatDisabled(player.getUniqueId())) continue; if (isGlobalChatDisabled(player.getUniqueId())) continue;
// Персонализируем сообщение для этого игрока (выделяем его ник) // Персонализируем сообщение для этого игрока (выделяем его ник)
String personalizedMsg = plugin.getMentionManager().getPersonalizedMessage(finalMessage, player); String personalizedMsg = plugin.getMentionManager().getPersonalizedMessage(processedMessage, player);
// Формируем итоговую строку // Формируем итоговую строку
String formatted = format String formatted = format
@ -61,34 +62,33 @@ public class ChatManager {
player.sendMessage(component); player.sendMessage(component);
// Проверяем упоминание ника без @ и уведомляем // Проверяем упоминание ника без @ и уведомляем
plugin.getMentionManager().notifyIfMentioned(message, player, sender); plugin.getMentionManager().notifyIfMentioned(messageText, player, sender);
} }
// Уведомление упомянутых через @ // Уведомление упомянутых через @
plugin.getMentionManager().notifyMentioned(mentionedPlayers); plugin.getMentionManager().notifyMentioned(mentionedPlayers);
plugin.getLogger().info("[G] " + sender.getName() + ": " + ChatFormatter.stripTags(message)); plugin.getLogger().info("[G] " + sender.getName() + ": " + ChatFormatter.stripTags(messageText));
} }
public void sendLocalMessage(Player sender, String message) { public void sendLocalMessage(Player sender, Component message) {
if (!plugin.getConfigManager().isLocalEnabled()) { if (!plugin.getConfigManager().isLocalEnabled()) {
return; return;
} }
// Конвертируем Component в String для обработки упоминаний
String messageText = PLAIN.serialize(message);
// Обработка упоминаний @ник // Обработка упоминаний @ник
Set<Player> mentionedPlayers = new HashSet<>(); Set<Player> mentionedPlayers = new HashSet<>();
String processedMessage = plugin.getMentionManager().processMentions(message, mentionedPlayers); String processedMessage = plugin.getMentionManager().processMentions(messageText, mentionedPlayers);
int radius = plugin.getConfigManager().getLocalRadius(); int radius = plugin.getConfigManager().getLocalRadius();
String format = plugin.getConfigManager().getLocalFormat(); String format = plugin.getConfigManager().getLocalFormat();
String prefix = plugin.getConfigManager().getLocalPrefix(); String prefix = plugin.getConfigManager().getLocalPrefix();
boolean hasColor = sender.hasPermission("chat.local.color");
// Получаем отображаемое имя игрока (с градиентом если есть) // Получаем отображаемое имя игрока (с градиентом если есть)
String playerDisplay = getPlayerDisplay(sender); String playerDisplay = getPlayerDisplay(sender);
// Обрабатываем сообщение игрока (смайлики, цвета если есть право)
String finalMessage = ChatFormatter.processPlayerMessage(processedMessage, sender, hasColor);
boolean foundRecipient = false; boolean foundRecipient = false;
Set<Player> recipients = new HashSet<>(); Set<Player> recipients = new HashSet<>();
@ -96,7 +96,7 @@ public class ChatManager {
for (Player player : Bukkit.getOnlinePlayers()) { for (Player player : Bukkit.getOnlinePlayers()) {
if (DistanceUtil.isInRange(sender, player, radius)) { if (DistanceUtil.isInRange(sender, player, radius)) {
// Персонализируем сообщение для этого игрока // Персонализируем сообщение для этого игрока
String personalizedMsg = plugin.getMentionManager().getPersonalizedMessage(finalMessage, player); String personalizedMsg = plugin.getMentionManager().getPersonalizedMessage(processedMessage, player);
// Формируем итоговую строку // Формируем итоговую строку
String formatted = prefix + " " + format String formatted = prefix + " " + format
@ -110,7 +110,7 @@ public class ChatManager {
recipients.add(player); recipients.add(player);
// Проверяем упоминание ника без @ // Проверяем упоминание ника без @
plugin.getMentionManager().notifyIfMentioned(message, player, sender); plugin.getMentionManager().notifyIfMentioned(messageText, player, sender);
if (!player.equals(sender)) { if (!player.equals(sender)) {
foundRecipient = true; foundRecipient = true;
@ -153,4 +153,4 @@ public class ChatManager {
public boolean isGlobalChatDisabled(UUID player) { public boolean isGlobalChatDisabled(UUID player) {
return globalChatDisabled.contains(player); return globalChatDisabled.contains(player);
} }
} }

View file

@ -2,13 +2,110 @@ package com.loki.lochat.utils;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.bukkit.entity.Player;
public final class ChatFormatter { public final class ChatFormatter {
private static final MiniMessage MM = MiniMessage.miniMessage(); private static final MiniMessage MM = MiniMessage.miniMessage();
private static final PlainTextComponentSerializer PLAIN =
PlainTextComponentSerializer.plainText();
private ChatFormatter() {} private ChatFormatter() {}
/* ===================== BASIC ===================== */
public static Component parse(String message) {
if (message == null) return Component.empty();
return MM.deserialize(message);
}
public static Component parse(String message, String... replacements) {
if (message == null) return Component.empty();
String result = message;
for (int i = 0; i + 1 < replacements.length; i += 2) {
result = result.replace(replacements[i], replacements[i + 1]);
}
return MM.deserialize(result);
}
public static String stripTags(String message) {
if (message == null) return "";
return PLAIN.serialize(MM.deserialize(message));
}
public static String toPlain(Component component) {
return component == null ? "" : PLAIN.serialize(component);
}
/* ===================== COLORS ===================== */
/**
* Legacy-совместимость.
* Сейчас просто возвращает MiniMessage-ready строку.
*/
public static String convertAllColors(String message) {
if (message == null) return "";
return message;
}
/* ===================== EMOJIS ===================== */
public static String replaceEmojis(String message) {
return message;
}
public static String replaceEmojis(String message, Player player) {
return message;
}
/* ===================== CHAT ===================== */
/**
* Legacy API для ChatManager
*/
public static String processPlayerMessage(
String message,
Player player,
boolean hasColorPermission
) {
return message;
}
/* ===================== ANNOUNCE ===================== */
public static Component formatAnnouncement(String format, String message) {
return parse(format.replace("<message>", message));
}
/* ===================== PM ===================== */
public static Component formatPmSentNew(
String format,
Player sender,
Player receiver,
String message
) {
return parse(format
.replace("{sender}", sender.getName())
.replace("{receiver}", receiver.getName())
.replace("{message}", message));
}
public static Component formatPmReceivedNew(
String format,
Player sender,
Player receiver,
String message
) {
return parse(format
.replace("{sender}", sender.getName())
.replace("{receiver}", receiver.getName())
.replace("{message}", message));
}
/* ===================== FORMAT (Component-based) ===================== */
public static Component format( public static Component format(
String format, String format,
Component name, Component name,