diff --git a/src/main/java/com/loki/lochat/api/ChatService.java b/src/main/java/com/loki/lochat/api/ChatService.java deleted file mode 100644 index 232b0c4..0000000 --- a/src/main/java/com/loki/lochat/api/ChatService.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.loki.lochat.api; - -import org.bukkit.entity.Player; - -/** - * Сервис для отправки сообщений в чат - */ -public interface ChatService { - void sendGlobalMessage(Player sender, Object message); - - void sendLocalMessage(Player sender, Object message); - - boolean toggleGlobalChat(java.util.UUID player); - - boolean isGlobalChatDisabled(java.util.UUID player); -} diff --git a/src/main/java/com/loki/lochat/api/CooldownService.java b/src/main/java/com/loki/lochat/api/CooldownService.java deleted file mode 100644 index 1f11b33..0000000 --- a/src/main/java/com/loki/lochat/api/CooldownService.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.loki.lochat.api; - -import java.util.UUID; - -/** - * Сервис для управления кулдаунами - */ -public interface CooldownService { - boolean isOnCooldown(UUID player, String type, int cooldownSeconds); - - int getRemainingCooldown(UUID player, String type, int cooldownSeconds); - - void setCooldown(UUID player, String type); - - void removeCooldown(UUID player); -} diff --git a/src/main/java/com/loki/lochat/api/MessageFilter.java b/src/main/java/com/loki/lochat/api/MessageFilter.java deleted file mode 100644 index 725f3ad..0000000 --- a/src/main/java/com/loki/lochat/api/MessageFilter.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.loki.lochat.api; - -import org.bukkit.entity.Player; - -/** - * Интерфейс для фильтрации сообщений - */ -public interface MessageFilter { - /** - * Применить фильтр к сообщению - * - * @return отфильтрованное сообщение или null если заблокировано - */ - Object apply(Player player, Object message, String plainMessage); -} diff --git a/src/main/java/com/loki/lochat/api/MuteService.java b/src/main/java/com/loki/lochat/api/MuteService.java deleted file mode 100644 index 0758051..0000000 --- a/src/main/java/com/loki/lochat/api/MuteService.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.loki.lochat.api; - -import java.util.UUID; - -/** - * Сервис для управления мутами - */ -public interface MuteService { - void mute(UUID uuid, String playerName, long duration, String reason, String mutedBy); - - boolean unmute(UUID uuid, String unmutedBy); - - boolean isMuted(UUID uuid); - - long getRemainingTime(UUID uuid); - - String formatTime(long millis); - - long parseTime(String timeStr); -} diff --git a/src/main/java/com/loki/lochat/commands/custom/MeCommand.java b/src/main/java/com/loki/lochat/commands/custom/MeCommand.java index 8c097e6..b2c0a17 100644 --- a/src/main/java/com/loki/lochat/commands/custom/MeCommand.java +++ b/src/main/java/com/loki/lochat/commands/custom/MeCommand.java @@ -47,7 +47,7 @@ public class MeCommand implements CommandExecutor { return true; } } catch (Exception e) { - // Сервис не зарегистрирован, продолжаем + } } diff --git a/src/main/java/com/loki/lochat/core/registry/ServiceRegistry.java b/src/main/java/com/loki/lochat/core/registry/ServiceRegistry.java index 55a4e61..4afbf30 100644 --- a/src/main/java/com/loki/lochat/core/registry/ServiceRegistry.java +++ b/src/main/java/com/loki/lochat/core/registry/ServiceRegistry.java @@ -17,26 +17,12 @@ public class ServiceRegistry { this.plugin = plugin; } - public static ServiceRegistry create(JavaPlugin plugin) { - ServiceRegistry registry = new ServiceRegistry(plugin); - registry.registerServices(); - return registry; - } - public static ServiceRegistry createWithDeps(JavaPlugin plugin, ConfigManager configManager, MessageConfig messageConfig) { ServiceRegistry registry = new ServiceRegistry(plugin); registry.registerServicesWithDeps(configManager, messageConfig); return registry; } - private void registerServices() { - register(ChatService.class, new ChatServiceImpl(plugin, this)); - register(MuteService.class, new MuteServiceImpl(plugin)); - register(CooldownService.class, new CooldownServiceImpl()); - register(MessageService.class, new MessageServiceImpl(plugin, this)); - register(PlayerDataService.class, new PlayerDataServiceImpl(plugin)); - } - private void registerServicesWithDeps(ConfigManager configManager, MessageConfig messageConfig) { register(ChatService.class, new ChatServiceImpl(plugin, this)); register(MuteService.class, new MuteServiceImpl(plugin)); diff --git a/src/main/java/com/loki/lochat/core/service/ChatServiceImpl.java b/src/main/java/com/loki/lochat/core/service/ChatServiceImpl.java index 243d4e9..0a0c39f 100644 --- a/src/main/java/com/loki/lochat/core/service/ChatServiceImpl.java +++ b/src/main/java/com/loki/lochat/core/service/ChatServiceImpl.java @@ -2,6 +2,7 @@ package com.loki.lochat.core.service; import com.loki.lochat.api.service.ChatService; import com.loki.lochat.core.registry.ServiceRegistry; +import com.loki.lochat.renderer.EnhancedChatRenderer; import com.loki.lochat.util.DistanceUtil; import com.loki.lochat.utils.ChatFormatter; import net.kyori.adventure.text.Component; @@ -26,26 +27,17 @@ public class ChatServiceImpl implements ChatService { @Override public void sendGlobalMessage(Player sender, Object message) { - String format = plugin.getConfig().getString("chat.global.format", - "{prefix} {player}: {message}"); - String prefix = plugin.getConfig().getString("chat.global.prefix", "[G]"); - Component messageComponent = (message instanceof Component) ? (Component) message : ChatFormatter.parse(message.toString()); - // Используем displayName игрока (с градиентом если есть) - Component playerName = sender.displayName(); - - String formatted = format.replace("{prefix}", prefix); - - Component finalComponent = ChatFormatter.parse(formatted) - .replaceText(builder -> builder.matchLiteral("{player}").replacement(playerName)) - .replaceText(builder -> builder.matchLiteral("{message}").replacement(messageComponent)); - + // Используем тот же рендерер что и в ChatEventListener для единообразия + EnhancedChatRenderer renderer = new EnhancedChatRenderer(plugin, sender, messageComponent, true); + for (Player player : Bukkit.getOnlinePlayers()) { if (!isGlobalChatDisabled(player.getUniqueId())) { - player.sendMessage(finalComponent); + Component renderedMessage = renderer.render(sender, sender.displayName(), messageComponent, player); + player.sendMessage(renderedMessage); } } } @@ -53,23 +45,18 @@ public class ChatServiceImpl implements ChatService { @Override public void sendLocalMessage(Player sender, Object message) { int radius = plugin.getConfig().getInt("chat.local.radius", 100); - String format = plugin.getConfig().getString("chat.local.format", - "{player}: {message}"); - + Component messageComponent = (message instanceof Component) ? (Component) message : ChatFormatter.parse(message.toString()); - // Используем displayName игрока (с градиентом если есть) - Component playerName = sender.displayName(); - - Component finalComponent = ChatFormatter.parse(format) - .replaceText(builder -> builder.matchLiteral("{player}").replacement(playerName)) - .replaceText(builder -> builder.matchLiteral("{message}").replacement(messageComponent)); + // Используем тот же рендерер что и в ChatEventListener для единообразия + EnhancedChatRenderer renderer = new EnhancedChatRenderer(plugin, sender, messageComponent, false); for (Player player : Bukkit.getOnlinePlayers()) { if (DistanceUtil.isInRange(sender, player, radius)) { - player.sendMessage(finalComponent); + Component renderedMessage = renderer.render(sender, sender.displayName(), messageComponent, player); + player.sendMessage(renderedMessage); } } } diff --git a/src/main/java/com/loki/lochat/core/service/IgnoreServiceImpl.java b/src/main/java/com/loki/lochat/core/service/IgnoreServiceImpl.java index 606c87a..e5cf033 100644 --- a/src/main/java/com/loki/lochat/core/service/IgnoreServiceImpl.java +++ b/src/main/java/com/loki/lochat/core/service/IgnoreServiceImpl.java @@ -1,7 +1,7 @@ package com.loki.lochat.core.service; import com.loki.lochat.api.service.IgnoreService; -import com.loki.lochat.gradient.util.FoliaUtil; +import com.loki.lochat.util.FoliaUtil; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.plugin.java.JavaPlugin; diff --git a/src/main/java/com/loki/lochat/core/service/MuteServiceImpl.java b/src/main/java/com/loki/lochat/core/service/MuteServiceImpl.java index 1ef717b..359a01a 100644 --- a/src/main/java/com/loki/lochat/core/service/MuteServiceImpl.java +++ b/src/main/java/com/loki/lochat/core/service/MuteServiceImpl.java @@ -5,7 +5,7 @@ import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; import com.loki.lochat.api.service.MuteService; import com.loki.lochat.data.model.MuteData; -import com.loki.lochat.gradient.util.FoliaUtil; +import com.loki.lochat.util.FoliaUtil; import com.loki.lochat.utils.TimeFormatter; import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; diff --git a/src/main/java/com/loki/lochat/gradient/commands/GradientAdminCommand.java b/src/main/java/com/loki/lochat/gradient/commands/GradientAdminCommand.java index ad488cf..cec9059 100644 --- a/src/main/java/com/loki/lochat/gradient/commands/GradientAdminCommand.java +++ b/src/main/java/com/loki/lochat/gradient/commands/GradientAdminCommand.java @@ -5,7 +5,7 @@ import com.loki.lochat.gradient.config.GradientConfig; import com.loki.lochat.gradient.config.GradientMessages; import com.loki.lochat.gradient.data.GradientPlayerData; import com.loki.lochat.gradient.util.DisplayNameUtil; -import com.loki.lochat.gradient.util.FoliaUtil; +import com.loki.lochat.util.FoliaUtil; import com.loki.lochat.gradient.util.GradientUtil; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; diff --git a/src/main/java/com/loki/lochat/gradient/commands/GradientColorCommand.java b/src/main/java/com/loki/lochat/gradient/commands/GradientColorCommand.java index f9bda89..0529e26 100644 --- a/src/main/java/com/loki/lochat/gradient/commands/GradientColorCommand.java +++ b/src/main/java/com/loki/lochat/gradient/commands/GradientColorCommand.java @@ -6,7 +6,7 @@ import com.loki.lochat.gradient.config.GradientMessages; import com.loki.lochat.gradient.data.GradientPlayerData; import com.loki.lochat.gradient.gui.GradientConfirmGUI; import com.loki.lochat.gradient.util.DisplayNameUtil; -import com.loki.lochat.gradient.util.FoliaUtil; +import com.loki.lochat.util.FoliaUtil; import com.loki.lochat.gradient.util.GradientConstants; import com.loki.lochat.gradient.util.GradientUtil; import org.bukkit.Bukkit; diff --git a/src/main/java/com/loki/lochat/gradient/commands/GradientPrefixCommand.java b/src/main/java/com/loki/lochat/gradient/commands/GradientPrefixCommand.java index 66a5bdb..b5ee511 100644 --- a/src/main/java/com/loki/lochat/gradient/commands/GradientPrefixCommand.java +++ b/src/main/java/com/loki/lochat/gradient/commands/GradientPrefixCommand.java @@ -6,7 +6,7 @@ import com.loki.lochat.gradient.config.GradientMessages; import com.loki.lochat.gradient.data.GradientPlayerData; import com.loki.lochat.gradient.gui.GradientConfirmGUI; import com.loki.lochat.gradient.util.DisplayNameUtil; -import com.loki.lochat.gradient.util.FoliaUtil; +import com.loki.lochat.util.FoliaUtil; import com.loki.lochat.gradient.util.GradientConstants; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; diff --git a/src/main/java/com/loki/lochat/gradient/listeners/GradientGUIListener.java b/src/main/java/com/loki/lochat/gradient/listeners/GradientGUIListener.java index 929a63f..5110902 100644 --- a/src/main/java/com/loki/lochat/gradient/listeners/GradientGUIListener.java +++ b/src/main/java/com/loki/lochat/gradient/listeners/GradientGUIListener.java @@ -5,7 +5,7 @@ import com.loki.lochat.gradient.config.GradientMessages; import com.loki.lochat.gradient.data.GradientPlayerData; import com.loki.lochat.gradient.gui.GradientConfirmGUI; import com.loki.lochat.gradient.util.DisplayNameUtil; -import com.loki.lochat.gradient.util.FoliaUtil; +import com.loki.lochat.util.FoliaUtil; import com.loki.lochat.gradient.util.GradientConstants; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; diff --git a/src/main/java/com/loki/lochat/gradient/listeners/GradientPlayerListener.java b/src/main/java/com/loki/lochat/gradient/listeners/GradientPlayerListener.java index e3a8df7..2ece0b1 100644 --- a/src/main/java/com/loki/lochat/gradient/listeners/GradientPlayerListener.java +++ b/src/main/java/com/loki/lochat/gradient/listeners/GradientPlayerListener.java @@ -1,7 +1,7 @@ package com.loki.lochat.gradient.listeners; import com.loki.lochat.gradient.GradientModule; -import com.loki.lochat.gradient.util.FoliaUtil; +import com.loki.lochat.util.FoliaUtil; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; diff --git a/src/main/java/com/loki/lochat/gradient/util/FoliaUtil.java b/src/main/java/com/loki/lochat/gradient/util/FoliaUtil.java deleted file mode 100644 index d3ccaf4..0000000 --- a/src/main/java/com/loki/lochat/gradient/util/FoliaUtil.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.loki.lochat.gradient.util; - -import org.bukkit.Bukkit; -import org.bukkit.entity.Entity; -import org.bukkit.plugin.Plugin; - -import java.util.concurrent.TimeUnit; - -/** - * Утилита для работы с Folia - */ -public final class FoliaUtil { - - private static final boolean IS_FOLIA; - - static { - IS_FOLIA = checkFolia(); - } - - private FoliaUtil() { - } - - private static boolean checkFolia() { - try { - Class.forName("io.papermc.paper.threadedregions.RegionizedServer"); - return true; - } catch (ClassNotFoundException e) { - return false; - } - } - - public static boolean isFolia() { - return IS_FOLIA; - } - - public static void runEntityTask(Plugin plugin, Entity entity, Runnable task) { - if (IS_FOLIA) { - entity.getScheduler().run(plugin, scheduledTask -> task.run(), null); - } else { - Bukkit.getScheduler().runTask(plugin, task); - } - } - - public static void runEntityTaskLater(Plugin plugin, Entity entity, Runnable task, long delayTicks) { - if (IS_FOLIA) { - entity.getScheduler().runDelayed(plugin, scheduledTask -> task.run(), null, delayTicks); - } else { - Bukkit.getScheduler().runTaskLater(plugin, task, delayTicks); - } - } - - public static void runAsync(Plugin plugin, Runnable task) { - if (IS_FOLIA) { - Bukkit.getAsyncScheduler().runNow(plugin, scheduledTask -> task.run()); - } else { - Bukkit.getScheduler().runTaskAsynchronously(plugin, task); - } - } - - public static void runAsyncLater(Plugin plugin, Runnable task, long delayMs) { - if (IS_FOLIA) { - Bukkit.getAsyncScheduler().runDelayed(plugin, scheduledTask -> task.run(), delayMs, TimeUnit.MILLISECONDS); - } else { - Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, task, delayMs / 50); - } - } - - public static void runGlobalTask(Plugin plugin, Runnable task) { - if (IS_FOLIA) { - Bukkit.getGlobalRegionScheduler().run(plugin, scheduledTask -> task.run()); - } else { - Bukkit.getScheduler().runTask(plugin, task); - } - } -} diff --git a/src/main/java/com/loki/lochat/listener/ChatEventListener.java b/src/main/java/com/loki/lochat/listener/ChatEventListener.java index fde76e9..53e5eb8 100644 --- a/src/main/java/com/loki/lochat/listener/ChatEventListener.java +++ b/src/main/java/com/loki/lochat/listener/ChatEventListener.java @@ -3,16 +3,8 @@ package com.loki.lochat.listener; import com.loki.lochat.api.service.MessageService; import com.loki.lochat.core.filter.AdvancedMessageFilter; import com.loki.lochat.core.registry.ServiceRegistry; -import com.loki.lochat.utils.MentionHandler; -import com.loki.lochat.utils.TextFormatter; -import io.papermc.paper.chat.ChatRenderer; +import com.loki.lochat.renderer.EnhancedChatRenderer; import io.papermc.paper.event.player.AsyncChatEvent; -import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.event.ClickEvent; -import net.kyori.adventure.text.event.HoverEvent; -import net.kyori.adventure.text.format.NamedTextColor; -import net.kyori.adventure.text.format.TextDecoration; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -24,19 +16,21 @@ public class ChatEventListener implements Listener { private final JavaPlugin plugin; private final MessageService messageService; private final AdvancedMessageFilter advancedFilter; - private final MentionHandler mentionHandler; public ChatEventListener(JavaPlugin plugin, ServiceRegistry registry) { this.plugin = plugin; this.messageService = registry.get(MessageService.class); this.advancedFilter = new AdvancedMessageFilter(plugin.getConfig()); - this.mentionHandler = new MentionHandler(plugin); } @EventHandler(priority = EventPriority.HIGHEST) public void onChat(AsyncChatEvent event) { Player sender = event.getPlayer(); - String message = PlainTextComponentSerializer.plainText().serialize(event.message()); + String rawMessage = PlainTextComponentSerializer.plainText().serialize(event.message()); + + // Определяем тип чата и очищаем сообщение от ! + boolean isGlobal = rawMessage.startsWith("!"); + String message = isGlobal ? rawMessage.substring(1).stripLeading() : rawMessage; boolean allowed = messageService.processMessage(sender, message); @@ -55,145 +49,16 @@ public class ChatEventListener implements Listener { String filteredMessage = filterResult.filteredMessage(); - // Определяем тип чата - boolean isGlobal = message.startsWith("!"); - event.message(com.loki.lochat.utils.ChatFormatter.parse(filteredMessage)); - event.renderer(new EnhancedChatRenderer(sender, event.message(), isGlobal)); + // Фильтруем получателей для локального чата + if (!isGlobal) { + int radius = plugin.getConfig().getInt("chat.local.radius", 100); + event.viewers().removeIf(viewer -> + viewer instanceof Player p && !com.loki.lochat.util.DistanceUtil.isInRange(sender, p, radius) + ); + } + + event.renderer(new EnhancedChatRenderer(plugin, sender, event.message(), isGlobal)); } - - private class EnhancedChatRenderer implements ChatRenderer { - private final Player sender; - private final Component originalMessage; - private final boolean isGlobal; - - public EnhancedChatRenderer(Player sender, Component originalMessage, boolean isGlobal) { - this.sender = sender; - this.originalMessage = originalMessage; - this.isGlobal = isGlobal; - } - - @Override - public Component render(Player source, Component sourceDisplayName, Component message, Audience viewer) { - // Префикс в зависимости от типа чата - Component prefixComponent; - if (isGlobal) { - // [G] с желтой буквой G - prefixComponent = Component.text("[") - .color(net.kyori.adventure.text.format.NamedTextColor.GRAY) - .append(Component.text("G").color(net.kyori.adventure.text.format.NamedTextColor.GOLD)) - .append(Component.text("]").color(net.kyori.adventure.text.format.NamedTextColor.GRAY)); - } else { - // [L] с голубой буквой L - prefixComponent = Component.text("[") - .color(net.kyori.adventure.text.format.NamedTextColor.GRAY) - .append(Component.text("L").color(net.kyori.adventure.text.format.NamedTextColor.AQUA)) - .append(Component.text("]").color(net.kyori.adventure.text.format.NamedTextColor.GRAY)); - } - - Component playerComponent = createPlayerComponent(source, sourceDisplayName); - Component processedMessage = message; - - processedMessage = TextFormatter.formatMarkdown(processedMessage); - processedMessage = processUrls(processedMessage); - processedMessage = processEmojis(processedMessage); - - if (viewer instanceof Player viewerPlayer) { - processedMessage = mentionHandler.processMentions(processedMessage, source, viewerPlayer); - } - - return prefixComponent - .append(Component.text(" ")) - .append(playerComponent) - .append(Component.text(": ").color(net.kyori.adventure.text.format.NamedTextColor.GRAY)) - .append(processedMessage); - } - - private Component createPlayerComponent(Player player, Component displayName) { - if (!plugin.getConfig().getBoolean("chat.hover.enabled", true)) { - return displayName.clickEvent(ClickEvent.suggestCommand("/msg " + player.getName() + " ")); - } - - java.util.List hoverLines = plugin.getConfig().getStringList("chat.hover.format"); - if (hoverLines.isEmpty()) { - hoverLines = java.util.Arrays.asList( - "Игрок: {player}", - "Мир: {world}", - "", - "▶ Клик: написать ЛС" - ); - } - - net.kyori.adventure.text.TextComponent.Builder hoverBuilder = Component.text(); - for (int i = 0; i < hoverLines.size(); i++) { - String line = hoverLines.get(i); - - line = line.replace("{player}", player.getName()) - .replace("{world}", player.getWorld().getName()) - .replace("{ping}", String.valueOf(player.getPing())) - .replace("{gamemode}", player.getGameMode().name()) - .replace("{health}", String.valueOf(Math.round(player.getHealth()))) - .replace("{food}", String.valueOf(player.getFoodLevel())); - - Component lineComponent = com.loki.lochat.utils.ChatFormatter.parse(line); - hoverBuilder.append(lineComponent); - - if (i < hoverLines.size() - 1) { - hoverBuilder.append(Component.newline()); - } - } - - return displayName - .hoverEvent(HoverEvent.showText(hoverBuilder.build())) - .clickEvent(ClickEvent.suggestCommand("/msg " + player.getName() + " ")); - } - - private Component processUrls(Component message) { - String plainText = PlainTextComponentSerializer.plainText().serialize(message); - // Fixed regex: escape the dash in character class to avoid illegal range - String urlPattern = "(https?://[\\w\\-._~:/?#\\[\\]@!$&'()*+,;=%]+)"; - java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(urlPattern); - java.util.regex.Matcher matcher = pattern.matcher(plainText); - - Component result = message; - while (matcher.find()) { - String url = matcher.group(1); - Component urlComponent = Component.text(url, NamedTextColor.AQUA, TextDecoration.UNDERLINED) - .hoverEvent(HoverEvent.showText(Component.text("Открыть: " + url, NamedTextColor.GRAY))) - .clickEvent(ClickEvent.openUrl(url)); - - result = result.replaceText(builder -> - builder.matchLiteral(url).replacement(urlComponent) - ); - } - - return result; - } - - private Component processEmojis(Component message) { - Component result = message; - result = result.replaceText(builder -> builder.matchLiteral(":heart:").replacement("❤")); - result = result.replaceText(builder -> builder.matchLiteral(":star:").replacement("★")); - result = result.replaceText(builder -> builder.matchLiteral(":check:").replacement("✔")); - result = result.replaceText(builder -> builder.matchLiteral(":cross:").replacement("✖")); - result = result.replaceText(builder -> builder.matchLiteral(":arrow:").replacement("→")); - result = result.replaceText(builder -> builder.matchLiteral(":skull:").replacement("☠")); - result = result.replaceText(builder -> builder.matchLiteral(":note:").replacement("♪")); - result = result.replaceText(builder -> builder.matchLiteral(":sun:").replacement("☀")); - result = result.replaceText(builder -> builder.matchLiteral(":moon:").replacement("☽")); - result = result.replaceText(builder -> builder.matchLiteral(":snowflake:").replacement("❄")); - result = result.replaceText(builder -> builder.matchLiteral(":fire:").replacement("🔥")); - result = result.replaceText(builder -> builder.matchLiteral(":diamond:").replacement("◆")); - result = result.replaceText(builder -> builder.matchLiteral(":sword:").replacement("⚔")); - result = result.replaceText(builder -> builder.matchLiteral(":pickaxe:").replacement("⛏")); - result = result.replaceText(builder -> builder.matchLiteral(":bow:").replacement("🏹")); - result = result.replaceText(builder -> builder.matchLiteral(":shield:").replacement("🛡")); - result = result.replaceText(builder -> builder.matchLiteral(":potion:").replacement("🧪")); - result = result.replaceText(builder -> builder.matchLiteral(":book:").replacement("📖")); - result = result.replaceText(builder -> builder.matchLiteral(":crown:").replacement("👑")); - result = result.replaceText(builder -> builder.matchLiteral(":gem:").replacement("💎")); - return result; - } - } -} +} \ No newline at end of file diff --git a/src/main/java/com/loki/lochat/managers/CustomCommandManager.java b/src/main/java/com/loki/lochat/managers/CustomCommandManager.java index 26da019..04ae7aa 100644 --- a/src/main/java/com/loki/lochat/managers/CustomCommandManager.java +++ b/src/main/java/com/loki/lochat/managers/CustomCommandManager.java @@ -71,10 +71,8 @@ public class CustomCommandManager { private void registerCommand(CustomCommandData data) { try { - // Получаем CommandMap через рефлексию - Field commandMapField = Bukkit.getServer().getClass().getDeclaredField("commandMap"); - commandMapField.setAccessible(true); - CommandMap commandMap = (CommandMap) commandMapField.get(Bukkit.getServer()); + // Получаем CommandMap через Paper API (безопаснее чем рефлексия) + CommandMap commandMap = Bukkit.getCommandMap(); // Создаем и регистрируем команду CustomCommand command = new CustomCommand(data.name, this); diff --git a/src/main/java/com/loki/lochat/renderer/EnhancedChatRenderer.java b/src/main/java/com/loki/lochat/renderer/EnhancedChatRenderer.java new file mode 100644 index 0000000..e42cd7a --- /dev/null +++ b/src/main/java/com/loki/lochat/renderer/EnhancedChatRenderer.java @@ -0,0 +1,155 @@ +package com.loki.lochat.renderer; + +import com.loki.lochat.utils.MentionHandler; +import com.loki.lochat.utils.TextFormatter; +import io.papermc.paper.chat.ChatRenderer; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; + +/** + * Рендерер чата с поддержкой глобального/локального режимов + */ +public class EnhancedChatRenderer implements ChatRenderer { + private final JavaPlugin plugin; + private final Player sender; + private final Component originalMessage; + private final boolean isGlobal; + private final MentionHandler mentionHandler; + + public EnhancedChatRenderer(JavaPlugin plugin, Player sender, Component originalMessage, boolean isGlobal) { + this.plugin = plugin; + this.sender = sender; + this.originalMessage = originalMessage; + this.isGlobal = isGlobal; + this.mentionHandler = new MentionHandler(plugin); + } + + @Override + public Component render(Player source, Component sourceDisplayName, Component message, Audience viewer) { + // Префикс в зависимости от типа чата + Component prefixComponent; + if (isGlobal) { + // [G] с желтой буквой G + prefixComponent = Component.text("[") + .color(net.kyori.adventure.text.format.NamedTextColor.GRAY) + .append(Component.text("G").color(net.kyori.adventure.text.format.NamedTextColor.GOLD)) + .append(Component.text("]").color(net.kyori.adventure.text.format.NamedTextColor.GRAY)); + } else { + // [L] с голубой буквой L + prefixComponent = Component.text("[") + .color(net.kyori.adventure.text.format.NamedTextColor.GRAY) + .append(Component.text("L").color(net.kyori.adventure.text.format.NamedTextColor.AQUA)) + .append(Component.text("]").color(net.kyori.adventure.text.format.NamedTextColor.GRAY)); + } + + Component playerComponent = createPlayerComponent(source, sourceDisplayName); + Component processedMessage = message; + + processedMessage = TextFormatter.formatMarkdown(processedMessage); + processedMessage = processUrls(processedMessage); + processedMessage = processEmojis(processedMessage); + + if (viewer instanceof Player viewerPlayer) { + processedMessage = mentionHandler.processMentions(processedMessage, source, viewerPlayer); + } + + return prefixComponent + .append(Component.text(" ")) + .append(playerComponent) + .append(Component.text(": ").color(net.kyori.adventure.text.format.NamedTextColor.GRAY)) + .append(processedMessage); + } + + private Component createPlayerComponent(Player player, Component displayName) { + if (!plugin.getConfig().getBoolean("chat.hover.enabled", true)) { + return displayName.clickEvent(ClickEvent.suggestCommand("/msg " + player.getName() + " ")); + } + + java.util.List hoverLines = plugin.getConfig().getStringList("chat.hover.format"); + if (hoverLines.isEmpty()) { + hoverLines = java.util.Arrays.asList( + "Игрок: {player}", + "Мир: {world}", + "", + "▶ Клик: написать ЛС" + ); + } + + net.kyori.adventure.text.TextComponent.Builder hoverBuilder = Component.text(); + for (int i = 0; i < hoverLines.size(); i++) { + String line = hoverLines.get(i); + + line = line.replace("{player}", player.getName()) + .replace("{world}", player.getWorld().getName()) + .replace("{ping}", String.valueOf(player.getPing())) + .replace("{gamemode}", player.getGameMode().name()) + .replace("{health}", String.valueOf(Math.round(player.getHealth()))) + .replace("{food}", String.valueOf(player.getFoodLevel())); + + Component lineComponent = com.loki.lochat.utils.ChatFormatter.parse(line); + hoverBuilder.append(lineComponent); + + if (i < hoverLines.size() - 1) { + hoverBuilder.append(Component.newline()); + } + } + + return displayName + .hoverEvent(HoverEvent.showText(hoverBuilder.build())) + .clickEvent(ClickEvent.suggestCommand("/msg " + player.getName() + " ")); + } + + private Component processUrls(Component message) { + String plainText = PlainTextComponentSerializer.plainText().serialize(message); + // Fixed regex: escape the dash in character class to avoid illegal range + String urlPattern = "(https?://[\\w\\-.~:/?#\\[\\]@!$&'()*+,;=%]+)"; + java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(urlPattern); + java.util.regex.Matcher matcher = pattern.matcher(plainText); + + Component result = message; + while (matcher.find()) { + String url = matcher.group(1); + Component urlComponent = Component.text(url, NamedTextColor.AQUA, TextDecoration.UNDERLINED) + .hoverEvent(HoverEvent.showText(Component.text("Открыть: " + url, NamedTextColor.GRAY))) + .clickEvent(ClickEvent.openUrl(url)); + + result = result.replaceText(builder -> + builder.matchLiteral(url).replacement(urlComponent) + ); + } + + return result; + } + + private Component processEmojis(Component message) { + Component result = message; + result = result.replaceText(builder -> builder.matchLiteral(":heart:").replacement("❤")); + result = result.replaceText(builder -> builder.matchLiteral(":star:").replacement("★")); + result = result.replaceText(builder -> builder.matchLiteral(":check:").replacement("✔")); + result = result.replaceText(builder -> builder.matchLiteral(":cross:").replacement("✖")); + result = result.replaceText(builder -> builder.matchLiteral(":arrow:").replacement("→")); + result = result.replaceText(builder -> builder.matchLiteral(":skull:").replacement("☠")); + result = result.replaceText(builder -> builder.matchLiteral(":note:").replacement("♪")); + result = result.replaceText(builder -> builder.matchLiteral(":sun:").replacement("☀")); + result = result.replaceText(builder -> builder.matchLiteral(":moon:").replacement("☽")); + result = result.replaceText(builder -> builder.matchLiteral(":snowflake:").replacement("❄")); + result = result.replaceText(builder -> builder.matchLiteral(":fire:").replacement("🔥")); + result = result.replaceText(builder -> builder.matchLiteral(":diamond:").replacement("◆")); + result = result.replaceText(builder -> builder.matchLiteral(":sword:").replacement("⚔")); + result = result.replaceText(builder -> builder.matchLiteral(":pickaxe:").replacement("⛏")); + result = result.replaceText(builder -> builder.matchLiteral(":bow:").replacement("🏹")); + result = result.replaceText(builder -> builder.matchLiteral(":shield:").replacement("🛡")); + result = result.replaceText(builder -> builder.matchLiteral(":potion:").replacement("🧪")); + result = result.replaceText(builder -> builder.matchLiteral(":book:").replacement("📖")); + result = result.replaceText(builder -> builder.matchLiteral(":crown:").replacement("👑")); + result = result.replaceText(builder -> builder.matchLiteral(":gem:").replacement("💎")); + return result; + } +} \ No newline at end of file diff --git a/src/main/java/com/loki/lochat/utils/DistanceUtil.java b/src/main/java/com/loki/lochat/utils/DistanceUtil.java deleted file mode 100644 index 2111444..0000000 --- a/src/main/java/com/loki/lochat/utils/DistanceUtil.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.loki.lochat.utils; - -import org.bukkit.Location; -import org.bukkit.entity.Player; - -public class DistanceUtil { - - private DistanceUtil() { - } - - public static boolean isInRange(Player player1, Player player2, int radius) { - // Проверка на один мир - if (!player1.getWorld().equals(player2.getWorld())) { - return false; - } - - Location loc1 = player1.getLocation(); - Location loc2 = player2.getLocation(); - - // Быстрая проверка по квадрату (без sqrt) - double dx = loc1.getX() - loc2.getX(); - double dz = loc1.getZ() - loc2.getZ(); - double radiusSquared = (double) radius * radius; - - return (dx * dx + dz * dz) <= radiusSquared; - } - - public static double getDistance(Player player1, Player player2) { - if (!player1.getWorld().equals(player2.getWorld())) { - return Double.MAX_VALUE; - } - return player1.getLocation().distance(player2.getLocation()); - } -}