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:
parent
8824ccd60f
commit
8c6fb2c92e
4 changed files with 88 additions and 67 deletions
BIN
src.zip
Normal file
BIN
src.zip
Normal file
Binary file not shown.
|
|
@ -5,6 +5,9 @@ import com.loki.lochat.managers.AntiSpamManager;
|
||||||
import com.loki.lochat.managers.FilterManager;
|
import com.loki.lochat.managers.FilterManager;
|
||||||
import com.loki.lochat.utils.ChatFormatter;
|
import com.loki.lochat.utils.ChatFormatter;
|
||||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.TextReplacementConfig;
|
||||||
|
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
|
|
@ -14,6 +17,7 @@ import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
public class ChatListener implements Listener {
|
public class ChatListener implements Listener {
|
||||||
|
|
||||||
private final LoChat plugin;
|
private final LoChat plugin;
|
||||||
|
private static final PlainTextComponentSerializer PLAIN = PlainTextComponentSerializer.plainText();
|
||||||
|
|
||||||
public ChatListener(LoChat plugin) {
|
public ChatListener(LoChat plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
|
@ -24,12 +28,14 @@ public class ChatListener implements Listener {
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
|
|
||||||
Player player = event.getPlayer();
|
Player player = event.getPlayer();
|
||||||
String message = net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer
|
Component message = event.message();
|
||||||
.plainText().serialize(event.message());
|
|
||||||
|
// Plain текст только для проверок
|
||||||
|
String plainMessage = PLAIN.serialize(message);
|
||||||
|
|
||||||
// Обработка через RegionScheduler для Folia
|
// Обработка через RegionScheduler для Folia
|
||||||
player.getScheduler().run(plugin, task -> {
|
player.getScheduler().run(plugin, task -> {
|
||||||
processChat(player, message);
|
processChat(player, plainMessage);
|
||||||
}, null);
|
}, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,11 +44,9 @@ public class ChatListener implements Listener {
|
||||||
if (plugin.getMuteManager().isMuted(player.getUniqueId())) {
|
if (plugin.getMuteManager().isMuted(player.getUniqueId())) {
|
||||||
String timeLeft = plugin.getMuteManager().formatRemainingTime(player.getUniqueId());
|
String timeLeft = plugin.getMuteManager().formatRemainingTime(player.getUniqueId());
|
||||||
if (timeLeft.equals("навсегда")) {
|
if (timeLeft.equals("навсегда")) {
|
||||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("mute.permanent")));
|
player.sendMessage(plugin.getMessageConfig().getComponent("mute.permanent"));
|
||||||
} else {
|
} else {
|
||||||
player.sendMessage(ChatFormatter.parse(
|
player.sendMessage(plugin.getMessageConfig().getComponent("mute.you-muted", "{time}", timeLeft));
|
||||||
plugin.getMessageConfig().get("mute.you-muted", "{time}", timeLeft)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -59,9 +63,7 @@ public class ChatListener implements Listener {
|
||||||
if (cooldown > 0 && !player.hasPermission("chat.bypass.cooldown")) {
|
if (cooldown > 0 && !player.hasPermission("chat.bypass.cooldown")) {
|
||||||
if (plugin.getCooldownManager().isOnCooldown(player.getUniqueId(), chatType, cooldown)) {
|
if (plugin.getCooldownManager().isOnCooldown(player.getUniqueId(), chatType, cooldown)) {
|
||||||
int remaining = plugin.getCooldownManager().getRemainingCooldown(player.getUniqueId(), chatType, cooldown);
|
int remaining = plugin.getCooldownManager().getRemainingCooldown(player.getUniqueId(), chatType, cooldown);
|
||||||
player.sendMessage(ChatFormatter.parse(
|
player.sendMessage(plugin.getMessageConfig().getComponent("cooldown.wait", "{time}", String.valueOf(remaining)));
|
||||||
plugin.getMessageConfig().get("cooldown.wait", "{time}", String.valueOf(remaining))
|
|
||||||
));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -71,15 +73,15 @@ public class ChatListener implements Listener {
|
||||||
AntiSpamManager.SpamResult spamResult = plugin.getAntiSpamManager().checkMessage(player.getUniqueId(), processedMessage);
|
AntiSpamManager.SpamResult spamResult = plugin.getAntiSpamManager().checkMessage(player.getUniqueId(), processedMessage);
|
||||||
switch (spamResult) {
|
switch (spamResult) {
|
||||||
case TOO_MANY_CAPS -> {
|
case TOO_MANY_CAPS -> {
|
||||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("antispam.caps")));
|
player.sendMessage(plugin.getMessageConfig().getComponent("antispam.caps"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case REPEAT_CHARS -> {
|
case REPEAT_CHARS -> {
|
||||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("antispam.repeat")));
|
player.sendMessage(plugin.getMessageConfig().getComponent("antispam.repeat"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case SIMILAR_MESSAGE -> {
|
case SIMILAR_MESSAGE -> {
|
||||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("antispam.similar")));
|
player.sendMessage(plugin.getMessageConfig().getComponent("antispam.similar"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -90,12 +92,11 @@ public class ChatListener implements Listener {
|
||||||
FilterManager.FilterResult filterResult = plugin.getFilterManager().checkMessage(processedMessage);
|
FilterManager.FilterResult filterResult = plugin.getFilterManager().checkMessage(processedMessage);
|
||||||
switch (filterResult) {
|
switch (filterResult) {
|
||||||
case BLOCKED -> {
|
case BLOCKED -> {
|
||||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("filter.blocked")));
|
player.sendMessage(plugin.getMessageConfig().getComponent("filter.blocked"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case WARNED -> {
|
case WARNED -> {
|
||||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("filter.warned")));
|
player.sendMessage(plugin.getMessageConfig().getComponent("filter.warned"));
|
||||||
// Продолжаем, но цензурим
|
|
||||||
processedMessage = plugin.getFilterManager().censorMessage(processedMessage);
|
processedMessage = plugin.getFilterManager().censorMessage(processedMessage);
|
||||||
}
|
}
|
||||||
case CENSORED -> processedMessage = plugin.getFilterManager().censorMessage(processedMessage);
|
case CENSORED -> processedMessage = plugin.getFilterManager().censorMessage(processedMessage);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import com.loki.lochat.LoChat;
|
||||||
import com.loki.lochat.utils.ChatFormatter;
|
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 org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
|
@ -16,6 +17,7 @@ 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();
|
||||||
|
|
||||||
public ChatManager(LoChat plugin) {
|
public ChatManager(LoChat plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
|
@ -23,7 +25,7 @@ public class ChatManager {
|
||||||
|
|
||||||
public void sendGlobalMessage(Player sender, String message) {
|
public void sendGlobalMessage(Player sender, String message) {
|
||||||
if (!plugin.getConfigManager().isGlobalEnabled()) {
|
if (!plugin.getConfigManager().isGlobalEnabled()) {
|
||||||
sender.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("global.disabled")));
|
sender.sendMessage(plugin.getMessageConfig().getComponent("global.disabled"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,15 +37,28 @@ public class ChatManager {
|
||||||
String prefix = plugin.getConfigManager().getGlobalPrefix();
|
String prefix = plugin.getConfigManager().getGlobalPrefix();
|
||||||
boolean hasColor = sender.hasPermission("chat.global.color");
|
boolean hasColor = sender.hasPermission("chat.global.color");
|
||||||
|
|
||||||
|
// Получаем отображаемое имя игрока (с градиентом если есть)
|
||||||
|
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(processedMessage, player);
|
String personalizedMsg = plugin.getMentionManager().getPersonalizedMessage(finalMessage, player);
|
||||||
|
|
||||||
Component formatted = ChatFormatter.formatGlobalMessage(format, prefix, sender, personalizedMsg, hasColor);
|
// Формируем итоговую строку
|
||||||
player.sendMessage(formatted);
|
String formatted = format
|
||||||
|
.replace("<prefix>", prefix)
|
||||||
|
.replace("<player>", playerDisplay)
|
||||||
|
.replace("<message>", personalizedMsg);
|
||||||
|
|
||||||
|
// Парсим в Component и отправляем
|
||||||
|
Component component = MINI_MESSAGE.deserialize(formatted);
|
||||||
|
player.sendMessage(component);
|
||||||
|
|
||||||
// Проверяем упоминание ника без @ и уведомляем
|
// Проверяем упоминание ника без @ и уведомляем
|
||||||
plugin.getMentionManager().notifyIfMentioned(message, player, sender);
|
plugin.getMentionManager().notifyIfMentioned(message, player, sender);
|
||||||
|
|
@ -69,16 +84,29 @@ public class ChatManager {
|
||||||
String prefix = plugin.getConfigManager().getLocalPrefix();
|
String prefix = plugin.getConfigManager().getLocalPrefix();
|
||||||
boolean hasColor = sender.hasPermission("chat.local.color");
|
boolean hasColor = sender.hasPermission("chat.local.color");
|
||||||
|
|
||||||
|
// Получаем отображаемое имя игрока (с градиентом если есть)
|
||||||
|
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<>();
|
||||||
|
|
||||||
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(processedMessage, player);
|
String personalizedMsg = plugin.getMentionManager().getPersonalizedMessage(finalMessage, player);
|
||||||
|
|
||||||
|
// Формируем итоговую строку
|
||||||
|
String formatted = prefix + " " + format
|
||||||
|
.replace("<player>", playerDisplay)
|
||||||
|
.replace("<message>", personalizedMsg);
|
||||||
|
|
||||||
|
// Парсим в Component и отправляем
|
||||||
|
Component component = MINI_MESSAGE.deserialize(formatted);
|
||||||
|
player.sendMessage(component);
|
||||||
|
|
||||||
Component formatted = ChatFormatter.formatLocalMessage(format, prefix, sender, personalizedMsg, hasColor);
|
|
||||||
player.sendMessage(formatted);
|
|
||||||
recipients.add(player);
|
recipients.add(player);
|
||||||
|
|
||||||
// Проверяем упоминание ника без @
|
// Проверяем упоминание ника без @
|
||||||
|
|
@ -95,10 +123,23 @@ public class ChatManager {
|
||||||
plugin.getMentionManager().notifyMentioned(mentionedPlayers);
|
plugin.getMentionManager().notifyMentioned(mentionedPlayers);
|
||||||
|
|
||||||
if (!foundRecipient) {
|
if (!foundRecipient) {
|
||||||
sender.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("local.no-players")));
|
sender.sendMessage(plugin.getMessageConfig().getComponent("local.no-players"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Получает отображаемое имя игрока с градиентом (если включено)
|
||||||
|
* Возвращает строку в MiniMessage формате
|
||||||
|
*/
|
||||||
|
private String getPlayerDisplay(Player player) {
|
||||||
|
if (plugin.getGradientModule() != null && plugin.getGradientModule().isEnabled()) {
|
||||||
|
String name = plugin.getGradientModule().getFormattedName(player);
|
||||||
|
// Конвертируем &#RRGGBB формат в MiniMessage
|
||||||
|
return ChatFormatter.convertAllColors(name);
|
||||||
|
}
|
||||||
|
return player.getName();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean toggleGlobalChat(UUID player) {
|
public boolean toggleGlobalChat(UUID player) {
|
||||||
if (globalChatDisabled.contains(player)) {
|
if (globalChatDisabled.contains(player)) {
|
||||||
globalChatDisabled.remove(player);
|
globalChatDisabled.remove(player);
|
||||||
|
|
|
||||||
|
|
@ -26,23 +26,29 @@ public class ChatFormatter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Конвертирует все форматы цветов в MiniMessage
|
* Конвертирует все форматы цветов в MiniMessage
|
||||||
* Поддерживает: &коды, §коды, &#HEX, §x§...HEX, MiniMessage теги
|
* Поддерживает: &коды, §коды, &#HEX, §x§...HEX
|
||||||
|
* Идемпотентный - не конвертирует уже конвертированные теги
|
||||||
*/
|
*/
|
||||||
public static String convertAllColors(String message) {
|
public static String convertAllColors(String message) {
|
||||||
if (message == null) return "";
|
if (message == null) return "";
|
||||||
|
|
||||||
|
// Если уже содержит MiniMessage теги <color:...> - не конвертируем повторно
|
||||||
|
if (message.contains("<color:#")) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
String result = message;
|
String result = message;
|
||||||
|
|
||||||
// 1. Конвертируем §x§R§R§G§G§B§B -> <#RRGGBB>
|
// 1. Конвертируем §x§R§R§G§G§B§B -> <color:#rrggbb>
|
||||||
result = convertSectionHexColors(result);
|
result = convertSectionHexColors(result);
|
||||||
|
|
||||||
// 2. Конвертируем §коды -> &коды (для унификации)
|
// 2. Конвертируем §коды -> &коды (для унификации)
|
||||||
result = convertSectionToAmpersand(result);
|
result = convertSectionToAmpersand(result);
|
||||||
|
|
||||||
// 3. Нормализуем существующие MiniMessage HEX теги в lowercase
|
// 3. Нормализуем существующие MiniMessage HEX теги <#RRGGBB> -> <color:#rrggbb>
|
||||||
result = normalizeMiniMessageHex(result);
|
result = normalizeMiniMessageHex(result);
|
||||||
|
|
||||||
// 4. Конвертируем &#RRGGBB -> <#RRGGBB> (только если еще не в MiniMessage формате)
|
// 4. Конвертируем &#RRGGBB -> <color:#rrggbb>
|
||||||
result = convertHexColors(result);
|
result = convertHexColors(result);
|
||||||
|
|
||||||
// 5. Конвертируем &коды -> MiniMessage теги
|
// 5. Конвертируем &коды -> MiniMessage теги
|
||||||
|
|
@ -232,10 +238,22 @@ public class ChatFormatter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Component parse(String message) {
|
public static Component parse(String message) {
|
||||||
|
return MINI_MESSAGE.deserialize(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Component parseWithColors(String message) {
|
||||||
return MINI_MESSAGE.deserialize(convertAllColors(message));
|
return MINI_MESSAGE.deserialize(convertAllColors(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Component parse(String message, String... replacements) {
|
public static Component parse(String message, String... replacements) {
|
||||||
|
String result = message;
|
||||||
|
for (int i = 0; i < replacements.length - 1; i += 2) {
|
||||||
|
result = result.replace(replacements[i], replacements[i + 1]);
|
||||||
|
}
|
||||||
|
return MINI_MESSAGE.deserialize(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Component parseWithColors(String message, String... replacements) {
|
||||||
String result = message;
|
String result = message;
|
||||||
for (int i = 0; i < replacements.length - 1; i += 2) {
|
for (int i = 0; i < replacements.length - 1; i += 2) {
|
||||||
result = result.replace(replacements[i], replacements[i + 1]);
|
result = result.replace(replacements[i], replacements[i + 1]);
|
||||||
|
|
@ -282,45 +300,6 @@ public class ChatFormatter {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Форматирует глобальное сообщение с поддержкой LoPreff
|
|
||||||
*/
|
|
||||||
public static Component formatGlobalMessage(String format, String prefix, Player player, String message, boolean hasColorPermission) {
|
|
||||||
String playerDisplay = getPlayerDisplay(player);
|
|
||||||
String finalMessage = processPlayerMessage(message, player, hasColorPermission);
|
|
||||||
|
|
||||||
String formatted = format
|
|
||||||
.replace("<prefix>", prefix)
|
|
||||||
.replace("<player>", playerDisplay)
|
|
||||||
.replace("<message>", finalMessage);
|
|
||||||
|
|
||||||
return parse(formatted);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Форматирует локальное сообщение с поддержкой LoPreff
|
|
||||||
*/
|
|
||||||
public static Component formatLocalMessage(String format, String prefix, Player player, String message, boolean hasColorPermission) {
|
|
||||||
String playerDisplay = getPlayerDisplay(player);
|
|
||||||
String finalMessage = processPlayerMessage(message, player, hasColorPermission);
|
|
||||||
|
|
||||||
String formatted = prefix + " " + format
|
|
||||||
.replace("<player>", playerDisplay)
|
|
||||||
.replace("<message>", finalMessage);
|
|
||||||
|
|
||||||
return parse(formatted);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getPlayerDisplay(Player player) {
|
|
||||||
LoChat plugin = LoChat.getInstance();
|
|
||||||
if (plugin != null && plugin.getGradientModule() != null && plugin.getGradientModule().isEnabled()) {
|
|
||||||
String name = plugin.getGradientModule().getFormattedName(player);
|
|
||||||
// Конвертируем §x§... формат в MiniMessage
|
|
||||||
return convertAllColors(name);
|
|
||||||
}
|
|
||||||
return player.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Component formatPmSent(String format, String targetName, String message) {
|
public static Component formatPmSent(String format, String targetName, String message) {
|
||||||
return parse(format
|
return parse(format
|
||||||
.replace("{player}", targetName)
|
.replace("{player}", targetName)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue