fix
This commit is contained in:
parent
49744acc59
commit
0fea0f4537
19 changed files with 192 additions and 377 deletions
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -47,7 +47,7 @@ public class MeCommand implements CommandExecutor {
|
|||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Сервис не зарегистрирован, продолжаем
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
||||
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;
|
||||
// Фильтруем получателей для локального чата
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
@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<String> hoverLines = plugin.getConfig().getStringList("chat.hover.format");
|
||||
if (hoverLines.isEmpty()) {
|
||||
hoverLines = java.util.Arrays.asList(
|
||||
"<gray>Игрок: <white>{player}",
|
||||
"<gray>Мир: <green>{world}",
|
||||
"",
|
||||
"<yellow>▶ Клик: написать ЛС"
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
event.renderer(new EnhancedChatRenderer(plugin, sender, event.message(), isGlobal));
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
155
src/main/java/com/loki/lochat/renderer/EnhancedChatRenderer.java
Normal file
155
src/main/java/com/loki/lochat/renderer/EnhancedChatRenderer.java
Normal file
|
|
@ -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<String> hoverLines = plugin.getConfig().getStringList("chat.hover.format");
|
||||
if (hoverLines.isEmpty()) {
|
||||
hoverLines = java.util.Arrays.asList(
|
||||
"<gray>Игрок: <white>{player}",
|
||||
"<gray>Мир: <green>{world}",
|
||||
"",
|
||||
"<yellow>▶ Клик: написать ЛС"
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue