diff --git a/src.zip b/src.zip new file mode 100644 index 0000000..e07272d Binary files /dev/null and b/src.zip differ diff --git a/src/main/java/com/loki/lochat/listeners/ChatListener.java b/src/main/java/com/loki/lochat/listeners/ChatListener.java index 758ca2c..89de658 100644 --- a/src/main/java/com/loki/lochat/listeners/ChatListener.java +++ b/src/main/java/com/loki/lochat/listeners/ChatListener.java @@ -5,6 +5,9 @@ import com.loki.lochat.managers.AntiSpamManager; import com.loki.lochat.managers.FilterManager; import com.loki.lochat.utils.ChatFormatter; 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.event.EventHandler; import org.bukkit.event.EventPriority; @@ -14,6 +17,7 @@ import org.bukkit.event.player.PlayerQuitEvent; public class ChatListener implements Listener { private final LoChat plugin; + private static final PlainTextComponentSerializer PLAIN = PlainTextComponentSerializer.plainText(); public ChatListener(LoChat plugin) { this.plugin = plugin; @@ -24,12 +28,14 @@ public class ChatListener implements Listener { event.setCancelled(true); Player player = event.getPlayer(); - String message = net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer - .plainText().serialize(event.message()); + Component message = event.message(); + + // Plain текст только для проверок + String plainMessage = PLAIN.serialize(message); // Обработка через RegionScheduler для Folia player.getScheduler().run(plugin, task -> { - processChat(player, message); + processChat(player, plainMessage); }, null); } @@ -38,11 +44,9 @@ public class ChatListener implements Listener { if (plugin.getMuteManager().isMuted(player.getUniqueId())) { String timeLeft = plugin.getMuteManager().formatRemainingTime(player.getUniqueId()); if (timeLeft.equals("навсегда")) { - player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("mute.permanent"))); + player.sendMessage(plugin.getMessageConfig().getComponent("mute.permanent")); } else { - player.sendMessage(ChatFormatter.parse( - plugin.getMessageConfig().get("mute.you-muted", "{time}", timeLeft) - )); + player.sendMessage(plugin.getMessageConfig().getComponent("mute.you-muted", "{time}", timeLeft)); } return; } @@ -59,9 +63,7 @@ public class ChatListener implements Listener { if (cooldown > 0 && !player.hasPermission("chat.bypass.cooldown")) { if (plugin.getCooldownManager().isOnCooldown(player.getUniqueId(), chatType, cooldown)) { int remaining = plugin.getCooldownManager().getRemainingCooldown(player.getUniqueId(), chatType, cooldown); - player.sendMessage(ChatFormatter.parse( - plugin.getMessageConfig().get("cooldown.wait", "{time}", String.valueOf(remaining)) - )); + player.sendMessage(plugin.getMessageConfig().getComponent("cooldown.wait", "{time}", String.valueOf(remaining))); return; } } @@ -71,15 +73,15 @@ public class ChatListener implements Listener { AntiSpamManager.SpamResult spamResult = plugin.getAntiSpamManager().checkMessage(player.getUniqueId(), processedMessage); switch (spamResult) { case TOO_MANY_CAPS -> { - player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("antispam.caps"))); + player.sendMessage(plugin.getMessageConfig().getComponent("antispam.caps")); return; } case REPEAT_CHARS -> { - player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("antispam.repeat"))); + player.sendMessage(plugin.getMessageConfig().getComponent("antispam.repeat")); return; } case SIMILAR_MESSAGE -> { - player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("antispam.similar"))); + player.sendMessage(plugin.getMessageConfig().getComponent("antispam.similar")); return; } } @@ -90,12 +92,11 @@ public class ChatListener implements Listener { FilterManager.FilterResult filterResult = plugin.getFilterManager().checkMessage(processedMessage); switch (filterResult) { case BLOCKED -> { - player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("filter.blocked"))); + player.sendMessage(plugin.getMessageConfig().getComponent("filter.blocked")); return; } case WARNED -> { - player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("filter.warned"))); - // Продолжаем, но цензурим + player.sendMessage(plugin.getMessageConfig().getComponent("filter.warned")); processedMessage = plugin.getFilterManager().censorMessage(processedMessage); } case CENSORED -> processedMessage = plugin.getFilterManager().censorMessage(processedMessage); diff --git a/src/main/java/com/loki/lochat/managers/ChatManager.java b/src/main/java/com/loki/lochat/managers/ChatManager.java index 653de0c..affb242 100644 --- a/src/main/java/com/loki/lochat/managers/ChatManager.java +++ b/src/main/java/com/loki/lochat/managers/ChatManager.java @@ -4,6 +4,7 @@ import com.loki.lochat.LoChat; import com.loki.lochat.utils.ChatFormatter; import com.loki.lochat.utils.DistanceUtil; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -16,6 +17,7 @@ public class ChatManager { private final LoChat plugin; private final Set globalChatDisabled = ConcurrentHashMap.newKeySet(); + private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage(); public ChatManager(LoChat plugin) { this.plugin = plugin; @@ -23,7 +25,7 @@ public class ChatManager { public void sendGlobalMessage(Player sender, String message) { if (!plugin.getConfigManager().isGlobalEnabled()) { - sender.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().get("global.disabled"))); + sender.sendMessage(plugin.getMessageConfig().getComponent("global.disabled")); return; } @@ -35,15 +37,28 @@ public class ChatManager { String prefix = plugin.getConfigManager().getGlobalPrefix(); boolean hasColor = sender.hasPermission("chat.global.color"); + // Получаем отображаемое имя игрока (с градиентом если есть) + String playerDisplay = getPlayerDisplay(sender); + + // Обрабатываем сообщение игрока (смайлики, цвета если есть право) + String finalMessage = ChatFormatter.processPlayerMessage(processedMessage, sender, hasColor); + // Отправка каждому игроку персонализированное сообщение for (Player player : Bukkit.getOnlinePlayers()) { 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) + .replace("", playerDisplay) + .replace("", personalizedMsg); + + // Парсим в Component и отправляем + Component component = MINI_MESSAGE.deserialize(formatted); + player.sendMessage(component); // Проверяем упоминание ника без @ и уведомляем plugin.getMentionManager().notifyIfMentioned(message, player, sender); @@ -69,16 +84,29 @@ public class ChatManager { String prefix = plugin.getConfigManager().getLocalPrefix(); boolean hasColor = sender.hasPermission("chat.local.color"); + // Получаем отображаемое имя игрока (с градиентом если есть) + String playerDisplay = getPlayerDisplay(sender); + + // Обрабатываем сообщение игрока (смайлики, цвета если есть право) + String finalMessage = ChatFormatter.processPlayerMessage(processedMessage, sender, hasColor); + boolean foundRecipient = false; Set recipients = new HashSet<>(); for (Player player : Bukkit.getOnlinePlayers()) { 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("", playerDisplay) + .replace("", 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); // Проверяем упоминание ника без @ @@ -95,10 +123,23 @@ public class ChatManager { plugin.getMentionManager().notifyMentioned(mentionedPlayers); 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) { if (globalChatDisabled.contains(player)) { globalChatDisabled.remove(player); diff --git a/src/main/java/com/loki/lochat/utils/ChatFormatter.java b/src/main/java/com/loki/lochat/utils/ChatFormatter.java index 622eaed..b1f84ab 100644 --- a/src/main/java/com/loki/lochat/utils/ChatFormatter.java +++ b/src/main/java/com/loki/lochat/utils/ChatFormatter.java @@ -26,23 +26,29 @@ public class ChatFormatter { /** * Конвертирует все форматы цветов в MiniMessage - * Поддерживает: &коды, §коды, &#HEX, §x§...HEX, MiniMessage теги + * Поддерживает: &коды, §коды, &#HEX, §x§...HEX + * Идемпотентный - не конвертирует уже конвертированные теги */ public static String convertAllColors(String message) { if (message == null) return ""; + // Если уже содержит MiniMessage теги - не конвертируем повторно + if (message.contains(" <#RRGGBB> + // 1. Конвертируем §x§R§R§G§G§B§B -> result = convertSectionHexColors(result); // 2. Конвертируем §коды -> &коды (для унификации) result = convertSectionToAmpersand(result); - // 3. Нормализуем существующие MiniMessage HEX теги в lowercase + // 3. Нормализуем существующие MiniMessage HEX теги <#RRGGBB> -> result = normalizeMiniMessageHex(result); - // 4. Конвертируем &#RRGGBB -> <#RRGGBB> (только если еще не в MiniMessage формате) + // 4. Конвертируем &#RRGGBB -> result = convertHexColors(result); // 5. Конвертируем &коды -> MiniMessage теги @@ -232,10 +238,22 @@ public class ChatFormatter { } public static Component parse(String message) { + return MINI_MESSAGE.deserialize(message); + } + + public static Component parseWithColors(String message) { return MINI_MESSAGE.deserialize(convertAllColors(message)); } 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; for (int i = 0; i < replacements.length - 1; i += 2) { result = result.replace(replacements[i], replacements[i + 1]); @@ -282,45 +300,6 @@ public class ChatFormatter { 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) - .replace("", playerDisplay) - .replace("", 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("", playerDisplay) - .replace("", 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) { return parse(format .replace("{player}", targetName)