Update to Minecraft 1.21.8, add checkstyle, fix compilation errors
- Update Paper API: 1.20.1 -> 1.21.4 - Update plugin version: 1.5.5-1.20.1 -> 1.5.5-1.21.8 - Add checkstyle plugin with reasonable rules - Fix Enchantment.LUCK -> Enchantment.UNBREAKING - Fix EntityType.FIREWORK -> EntityType.FIREWORK_ROCKET - Remove unused imports - Add temp_*/ to .gitignore - 118 files changed, 1633 insertions(+), 1301 deletions(-)
This commit is contained in:
parent
6a4c8a7886
commit
e78d8a2de4
121 changed files with 1792 additions and 1301 deletions
|
|
@ -31,71 +31,76 @@ public final class LoChat extends JavaPlugin {
|
|||
private AutoMessageManager autoMessageManager;
|
||||
private CustomCommandManager customCommandManager;
|
||||
|
||||
// Геттеры
|
||||
public static LoChat getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
instance = this;
|
||||
getLogger().info("Загрузка LoChat v" + getPluginMeta().getVersion() + " для Paper/Folia " + Bukkit.getMinecraftVersion() + "...");
|
||||
|
||||
|
||||
// Конфиги
|
||||
configManager = new ConfigManager(this);
|
||||
messageConfig = new MessageConfig(this);
|
||||
|
||||
|
||||
// Service Registry (SOLID DI) - с зависимостями
|
||||
serviceRegistry = new ServiceRegistry(this, configManager, messageConfig);
|
||||
serviceRegistry = ServiceRegistry.createWithDeps(this, configManager, messageConfig);
|
||||
getLogger().info("ServiceRegistry инициализирован (SOLID DI)");
|
||||
|
||||
|
||||
// Менеджеры
|
||||
customCommandManager = new CustomCommandManager(this);
|
||||
customCommandManager = CustomCommandManager.create(this);
|
||||
autoMessageManager = new AutoMessageManager(this);
|
||||
|
||||
|
||||
// Интеграции
|
||||
initIntegrations();
|
||||
|
||||
|
||||
// Команды и слушатели
|
||||
registerCommands();
|
||||
registerListeners();
|
||||
|
||||
|
||||
// Автосообщения
|
||||
autoMessageManager.start();
|
||||
|
||||
|
||||
getLogger().info("LoChat успешно запущен!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (autoMessageManager != null) autoMessageManager.stop();
|
||||
|
||||
|
||||
// Сохранение данных через сервисы
|
||||
IgnoreService ignoreService = serviceRegistry.get(IgnoreService.class);
|
||||
if (ignoreService != null) ignoreService.save();
|
||||
|
||||
|
||||
MuteService muteService = serviceRegistry.get(MuteService.class);
|
||||
if (muteService != null) muteService.save();
|
||||
|
||||
|
||||
NickService nickService = serviceRegistry.get(NickService.class);
|
||||
if (nickService != null) nickService.save();
|
||||
|
||||
|
||||
if (gradientModule != null) gradientModule.shutdown();
|
||||
getLogger().info("LoChat отключен!");
|
||||
}
|
||||
|
||||
|
||||
private void initIntegrations() {
|
||||
// Gradient модуль
|
||||
gradientModule = new GradientModule(this);
|
||||
if (gradientModule.init()) {
|
||||
getLogger().info("Gradient модуль (LoPreff) загружен!");
|
||||
}
|
||||
|
||||
|
||||
// LibertyBans
|
||||
new LibertyBansHook(this);
|
||||
|
||||
|
||||
// PlaceholderAPI
|
||||
if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) {
|
||||
new PlaceholderAPIHook(this).register();
|
||||
getLogger().info("PlaceholderAPI подключен!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void registerCommands() {
|
||||
getCommand("g").setExecutor(new GlobalChatCommand(this));
|
||||
getCommand("l").setExecutor(new LocalChatCommand(this));
|
||||
|
|
@ -106,44 +111,44 @@ public final class LoChat extends JavaPlugin {
|
|||
getCommand("announce").setExecutor(new AnnounceCommand(this));
|
||||
getCommand("chatspy").setExecutor(new ChatSpyCommand(this));
|
||||
getCommand("clearchat").setExecutor(new ClearChatCommand(this));
|
||||
|
||||
|
||||
NickCommand nickCmd = new NickCommand(this);
|
||||
getCommand("nick").setExecutor(nickCmd);
|
||||
getCommand("nick").setTabCompleter(nickCmd);
|
||||
|
||||
|
||||
LoChatCommand loChatCmd = new LoChatCommand(this);
|
||||
getCommand("lochat").setExecutor(loChatCmd);
|
||||
getCommand("lochat").setTabCompleter(loChatCmd);
|
||||
|
||||
|
||||
MuteCommand muteCmd = new MuteCommand(this);
|
||||
getCommand("lmute").setExecutor(muteCmd);
|
||||
getCommand("lmute").setTabCompleter(muteCmd);
|
||||
|
||||
|
||||
UnmuteCommand unmuteCmd = new UnmuteCommand(this);
|
||||
getCommand("lunmute").setExecutor(unmuteCmd);
|
||||
getCommand("lunmute").setTabCompleter(unmuteCmd);
|
||||
|
||||
|
||||
getCommand("lmutelist").setExecutor(new MuteListCommand(this));
|
||||
|
||||
|
||||
MuteHistoryCommand historyCmd = new MuteHistoryCommand(this);
|
||||
getCommand("lmutehistory").setExecutor(historyCmd);
|
||||
getCommand("lmutehistory").setTabCompleter(historyCmd);
|
||||
|
||||
|
||||
MuteBlameCommand blameCmd = new MuteBlameCommand(this);
|
||||
getCommand("lmuteblame").setExecutor(blameCmd);
|
||||
getCommand("lmuteblame").setTabCompleter(blameCmd);
|
||||
|
||||
|
||||
ClearChatConfigCommand clearCfgCmd = new ClearChatConfigCommand(this);
|
||||
getCommand("clearchatconfig").setExecutor(clearCfgCmd);
|
||||
getCommand("clearchatconfig").setTabCompleter(clearCfgCmd);
|
||||
}
|
||||
|
||||
|
||||
private void registerListeners() {
|
||||
getServer().getPluginManager().registerEvents(new ChatEventListener(this, serviceRegistry), this);
|
||||
getServer().getPluginManager().registerEvents(new PlayerEventListener(serviceRegistry), this);
|
||||
getLogger().info("Слушатели зарегистрированы (новая архитектура)");
|
||||
}
|
||||
|
||||
|
||||
public void reload() {
|
||||
configManager.reload();
|
||||
messageConfig.reload();
|
||||
|
|
@ -152,11 +157,23 @@ public final class LoChat extends JavaPlugin {
|
|||
if (customCommandManager != null) customCommandManager.reload();
|
||||
}
|
||||
|
||||
// Геттеры
|
||||
public static LoChat getInstance() { return instance; }
|
||||
public ServiceRegistry getServiceRegistry() { return serviceRegistry; }
|
||||
public ConfigManager getConfigManager() { return configManager; }
|
||||
public MessageConfig getMessageConfig() { return messageConfig; }
|
||||
public GradientModule getGradientModule() { return gradientModule; }
|
||||
public CustomCommandManager getCustomCommandManager() { return customCommandManager; }
|
||||
public ServiceRegistry getServiceRegistry() {
|
||||
return serviceRegistry;
|
||||
}
|
||||
|
||||
public ConfigManager getConfigManager() {
|
||||
return configManager;
|
||||
}
|
||||
|
||||
public MessageConfig getMessageConfig() {
|
||||
return messageConfig;
|
||||
}
|
||||
|
||||
public GradientModule getGradientModule() {
|
||||
return gradientModule;
|
||||
}
|
||||
|
||||
public CustomCommandManager getCustomCommandManager() {
|
||||
return customCommandManager;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@ 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@ 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import org.bukkit.entity.Player;
|
|||
public interface MessageFilter {
|
||||
/**
|
||||
* Применить фильтр к сообщению
|
||||
*
|
||||
* @return отфильтрованное сообщение или null если заблокировано
|
||||
*/
|
||||
Object apply(Player player, Object message, String plainMessage);
|
||||
|
|
|
|||
|
|
@ -7,9 +7,14 @@ 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import org.bukkit.entity.Player;
|
|||
public interface MessageFilter {
|
||||
/**
|
||||
* Применить фильтр к сообщению
|
||||
*
|
||||
* @return true если сообщение прошло фильтр, false если заблокировано
|
||||
*/
|
||||
boolean apply(Player player, ChatMessage message);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ import java.util.UUID;
|
|||
*/
|
||||
public interface ChatService {
|
||||
void sendGlobalMessage(Player sender, Object message);
|
||||
|
||||
void sendLocalMessage(Player sender, Object message);
|
||||
|
||||
boolean toggleGlobalChat(UUID player);
|
||||
|
||||
boolean isGlobalChatDisabled(UUID player);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@ 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,39 +11,41 @@ public interface IgnoreService {
|
|||
* Проверяет, игнорирует ли игрок другого
|
||||
*/
|
||||
boolean isIgnoring(UUID player, UUID target);
|
||||
|
||||
|
||||
/**
|
||||
* Добавляет игрока в список игнорируемых
|
||||
*
|
||||
* @return true если успешно добавлен
|
||||
*/
|
||||
boolean addIgnore(UUID player, UUID target);
|
||||
|
||||
|
||||
/**
|
||||
* Удаляет игрока из списка игнорируемых
|
||||
*
|
||||
* @return true если успешно удалён
|
||||
*/
|
||||
boolean removeIgnore(UUID player, UUID target);
|
||||
|
||||
|
||||
/**
|
||||
* Получает список всех игнорируемых игроков
|
||||
*/
|
||||
Set<UUID> getIgnoredPlayers(UUID player);
|
||||
|
||||
|
||||
/**
|
||||
* Получает количество игнорируемых игроков
|
||||
*/
|
||||
int getIgnoredCount(UUID player);
|
||||
|
||||
|
||||
/**
|
||||
* Очищает список игнорируемых для игрока
|
||||
*/
|
||||
void clearIgnores(UUID player);
|
||||
|
||||
|
||||
/**
|
||||
* Сохраняет данные на диск
|
||||
*/
|
||||
void save();
|
||||
|
||||
|
||||
/**
|
||||
* Загружает данные с диска
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -10,26 +10,27 @@ import java.util.Set;
|
|||
public interface MentionService {
|
||||
/**
|
||||
* Обрабатывает упоминания в сообщении
|
||||
*
|
||||
* @return сообщение с подсвеченными никами
|
||||
*/
|
||||
String processMentions(String message, Set<Player> mentionedPlayers);
|
||||
|
||||
|
||||
/**
|
||||
* Создаёт персонализированное сообщение для игрока
|
||||
* Если его ник упомянут — выделяет его особым образом
|
||||
*/
|
||||
String getPersonalizedMessage(String message, Player viewer);
|
||||
|
||||
|
||||
/**
|
||||
* Проверяет, упомянут ли игрок в сообщении (по нику без @)
|
||||
*/
|
||||
boolean isPlayerMentioned(String message, Player player);
|
||||
|
||||
|
||||
/**
|
||||
* Уведомляет упомянутых игроков звуком
|
||||
*/
|
||||
void notifyMentioned(Set<Player> players);
|
||||
|
||||
|
||||
/**
|
||||
* Уведомляет игрока если его ник упомянут (без @)
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import org.bukkit.entity.Player;
|
|||
public interface MessageService {
|
||||
/**
|
||||
* Обработать входящее сообщение чата
|
||||
*
|
||||
* @return true если сообщение обработано успешно
|
||||
*/
|
||||
boolean processMessage(Player player, String message);
|
||||
|
|
|
|||
|
|
@ -13,30 +13,38 @@ 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);
|
||||
|
||||
MuteData getMuteData(UUID uuid);
|
||||
|
||||
long getRemainingTime(UUID uuid);
|
||||
|
||||
|
||||
// Утилиты времени
|
||||
String formatTime(long millis);
|
||||
|
||||
long parseTime(String timeStr);
|
||||
|
||||
|
||||
// Права доступа
|
||||
long getMaxDuration(Player player);
|
||||
|
||||
boolean canMuteForDuration(Player player, long duration);
|
||||
|
||||
|
||||
// История
|
||||
List<MuteData.MuteHistoryEntry> getPlayerHistory(UUID uuid);
|
||||
|
||||
List<MuteData.MuteHistoryEntry> getMutesByOperator(String operatorName);
|
||||
|
||||
UUID getUUIDByName(String name);
|
||||
|
||||
|
||||
// Список активных мутов
|
||||
Map<UUID, MuteData> getActiveMutes();
|
||||
|
||||
|
||||
// Форматирование сообщений
|
||||
String formatMessage(String message, String player, String operator, String duration, String reason);
|
||||
|
||||
|
||||
// Сохранение
|
||||
void save();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,48 +9,54 @@ import java.util.UUID;
|
|||
* Сервис для управления кастомными никами
|
||||
*/
|
||||
public interface NickService {
|
||||
|
||||
|
||||
/**
|
||||
* Устанавливает кастомный ник игроку
|
||||
* @param player UUID игрока
|
||||
*
|
||||
* @param player UUID игрока
|
||||
* @param nickname Новый ник (с поддержкой цветов и русских символов)
|
||||
* @return true если успешно, false если ник занят или невалиден
|
||||
*/
|
||||
boolean setNickname(UUID player, String nickname);
|
||||
|
||||
|
||||
/**
|
||||
* Сбрасывает кастомный ник игрока
|
||||
*
|
||||
* @param player UUID игрока
|
||||
*/
|
||||
void resetNickname(UUID player);
|
||||
|
||||
|
||||
/**
|
||||
* Получает кастомный ник игрока
|
||||
*
|
||||
* @param player UUID игрока
|
||||
* @return Optional с ником или empty если нет кастомного ника
|
||||
*/
|
||||
Optional<String> getNickname(UUID player);
|
||||
|
||||
|
||||
/**
|
||||
* Проверяет занят ли ник
|
||||
*
|
||||
* @param nickname Ник для проверки
|
||||
* @return true если ник уже используется
|
||||
*/
|
||||
boolean isNicknameTaken(String nickname);
|
||||
|
||||
|
||||
/**
|
||||
* Валидирует ник (длина, символы)
|
||||
*
|
||||
* @param nickname Ник для проверки
|
||||
* @return true если ник валиден
|
||||
*/
|
||||
boolean isValidNickname(String nickname);
|
||||
|
||||
|
||||
/**
|
||||
* Обновляет displayName и tabName игрока
|
||||
*
|
||||
* @param player Игрок
|
||||
*/
|
||||
void updatePlayerDisplay(Player player);
|
||||
|
||||
|
||||
/**
|
||||
* Сохраняет данные
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -11,17 +11,17 @@ public interface PMService {
|
|||
* Устанавливает последнего собеседника для игрока
|
||||
*/
|
||||
void setLastConversation(UUID player, UUID target);
|
||||
|
||||
|
||||
/**
|
||||
* Получает последнего собеседника игрока
|
||||
*/
|
||||
Optional<UUID> getLastConversation(UUID player);
|
||||
|
||||
|
||||
/**
|
||||
* Удаляет информацию о последнем разговоре
|
||||
*/
|
||||
void removeConversation(UUID player);
|
||||
|
||||
|
||||
/**
|
||||
* Проверяет, есть ли у игрока активный разговор
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -7,5 +7,6 @@ import java.util.UUID;
|
|||
*/
|
||||
public interface PlayerDataService {
|
||||
void clearPlayerData(UUID player);
|
||||
|
||||
void saveAll();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,25 +9,26 @@ import org.bukkit.entity.Player;
|
|||
public interface SpyService {
|
||||
/**
|
||||
* Переключает режим шпиона для игрока
|
||||
*
|
||||
* @return true если шпион включен, false если выключен
|
||||
*/
|
||||
boolean toggleSpy(java.util.UUID player);
|
||||
|
||||
|
||||
/**
|
||||
* Проверяет, находится ли игрок в режиме шпиона
|
||||
*/
|
||||
boolean isSpying(java.util.UUID player);
|
||||
|
||||
|
||||
/**
|
||||
* Отправляет сообщение всем шпионам (для обычного чата)
|
||||
*/
|
||||
void sendToSpies(Player sender, Component message, boolean isGlobal);
|
||||
|
||||
|
||||
/**
|
||||
* Отправляет информацию о личном сообщении шпионам
|
||||
*/
|
||||
void broadcastPM(Player sender, Player receiver, String message);
|
||||
|
||||
|
||||
/**
|
||||
* Удаляет игрока из списка шпионов
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -42,20 +42,20 @@ public class AnnounceCommand implements CommandExecutor {
|
|||
// Отправка в чат
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
player.sendMessage(formatted);
|
||||
|
||||
|
||||
// ActionBar (если включен)
|
||||
if (plugin.getConfigManager().isAnnouncementActionBarEnabled()) {
|
||||
player.sendActionBar(formatted);
|
||||
}
|
||||
|
||||
|
||||
// Title (если включен)
|
||||
if (plugin.getConfigManager().isAnnouncementTitleEnabled()) {
|
||||
Title title = Title.title(
|
||||
ChatFormatter.parse(plugin.getConfigManager().getAnnouncementTitleHeader()),
|
||||
ChatFormatter.parse(message),
|
||||
Title.Times.times(
|
||||
Duration.ofMillis(500),
|
||||
Duration.ofSeconds(plugin.getConfigManager().getAnnouncementTitleDuration()),
|
||||
Duration.ofMillis(500),
|
||||
Duration.ofSeconds(plugin.getConfigManager().getAnnouncementTitleDuration()),
|
||||
Duration.ofMillis(500)
|
||||
)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public class ClearChatCommand implements CommandExecutor {
|
|||
for (int i = 0; i < com.loki.lochat.utils.Constants.CLEAR_CHAT_LINES; i++) {
|
||||
player.sendMessage("");
|
||||
}
|
||||
|
||||
|
||||
// Уведомление (если включено)
|
||||
if (plugin.getConfigManager().isClearChatMessageEnabled()) {
|
||||
String message = plugin.getConfigManager().getClearChatMessage();
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ public class ClearChatConfigCommand implements CommandExecutor, TabCompleter {
|
|||
sender.sendMessage(ChatFormatter.parse("&cУкажите текст сообщения!"));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
String message = String.join(" ", Arrays.copyOfRange(args, 1, args.length));
|
||||
plugin.getConfigManager().setClearChatMessage(message);
|
||||
sender.sendMessage(ChatFormatter.parse("&aСообщение установлено: &f" + message));
|
||||
|
|
@ -64,7 +64,7 @@ public class ClearChatConfigCommand implements CommandExecutor, TabCompleter {
|
|||
case "status" -> {
|
||||
boolean enabled = plugin.getConfigManager().isClearChatMessageEnabled();
|
||||
String message = plugin.getConfigManager().getClearChatMessage();
|
||||
|
||||
|
||||
sender.sendMessage(ChatFormatter.parse("&6Статус сообщения очистки чата:"));
|
||||
sender.sendMessage(ChatFormatter.parse("&eВключено: " + (enabled ? "&aДа" : "&cНет")));
|
||||
sender.sendMessage(ChatFormatter.parse("&eСообщение: &f" + message));
|
||||
|
|
|
|||
|
|
@ -9,17 +9,13 @@ import org.jetbrains.annotations.NotNull;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CustomCommand extends Command {
|
||||
public final class CustomCommand extends Command {
|
||||
|
||||
private final CustomCommandManager manager;
|
||||
|
||||
public CustomCommand(String name, CustomCommandManager manager) {
|
||||
super(name);
|
||||
this.manager = manager;
|
||||
init(name);
|
||||
}
|
||||
|
||||
private void init(String name) {
|
||||
this.setDescription("Кастомная команда LoChat");
|
||||
this.setUsage("/" + name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ import org.jetbrains.annotations.NotNull;
|
|||
*/
|
||||
public class GlobalChatCommand implements CommandExecutor {
|
||||
|
||||
private static final MiniMessage MM = MiniMessage.miniMessage();
|
||||
private final LoChat plugin;
|
||||
private final ChatService chatService;
|
||||
private static final MiniMessage MM = MiniMessage.miniMessage();
|
||||
|
||||
public GlobalChatCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
|
|
@ -53,10 +53,10 @@ public class GlobalChatCommand implements CommandExecutor {
|
|||
}
|
||||
|
||||
String message = String.join(" ", args);
|
||||
|
||||
|
||||
// Конвертируем String в Component для новой сигнатуры ChatService
|
||||
Component messageComponent = MM.deserialize(message);
|
||||
|
||||
|
||||
chatService.sendGlobalMessage(player, messageComponent);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ import java.util.List;
|
|||
|
||||
public class LoChatCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final LoChat plugin;
|
||||
private static final MiniMessage MM = MiniMessage.miniMessage();
|
||||
private final LoChat plugin;
|
||||
|
||||
public LoChatCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
|
|
@ -51,14 +51,15 @@ public class LoChatCommand implements CommandExecutor, TabCompleter {
|
|||
var commands = plugin.getCustomCommandManager().getCommands();
|
||||
sender.sendMessage(MM.deserialize("&#FFD700Кастомные команды (" + commands.size() + "):"));
|
||||
for (var cmd : commands.values()) {
|
||||
sender.sendMessage(MM.deserialize("&e/" + cmd.name + " 󅒐(" + cmd.type + ")"));
|
||||
if (!cmd.aliases.isEmpty()) {
|
||||
sender.sendMessage(MM.deserialize("󅒐 Алиасы: " + String.join(", ", cmd.aliases)));
|
||||
sender.sendMessage(MM.deserialize("&e/" + cmd.name() + " 󅒐(" + cmd.type() + ")"));
|
||||
if (!cmd.aliases().isEmpty()) {
|
||||
sender.sendMessage(MM.deserialize("󅒐 Алиасы: " + String.join(", ", cmd.aliases())));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
default -> sender.sendMessage(MM.deserialize("&#FF0000Неизвестная команда. Используйте /lochat для помощи."));
|
||||
default ->
|
||||
sender.sendMessage(MM.deserialize("&#FF0000Неизвестная команда. Используйте /lochat для помощи."));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -66,7 +67,7 @@ public class LoChatCommand implements CommandExecutor, TabCompleter {
|
|||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
if (!sender.hasPermission("lochat.admin")) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
|
@ -74,7 +75,7 @@ public class LoChatCommand implements CommandExecutor, TabCompleter {
|
|||
if (args.length == 1) {
|
||||
List<String> completions = new ArrayList<>();
|
||||
String input = args[0].toLowerCase();
|
||||
|
||||
|
||||
for (String cmd : List.of("reload", "commands")) {
|
||||
if (cmd.startsWith(input)) {
|
||||
completions.add(cmd);
|
||||
|
|
@ -82,11 +83,11 @@ public class LoChatCommand implements CommandExecutor, TabCompleter {
|
|||
}
|
||||
return completions;
|
||||
}
|
||||
|
||||
|
||||
if (args.length == 2 && args[0].equalsIgnoreCase("commands")) {
|
||||
List<String> completions = new ArrayList<>();
|
||||
String input = args[1].toLowerCase();
|
||||
|
||||
|
||||
if ("reload".startsWith(input)) {
|
||||
completions.add("reload");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ import org.jetbrains.annotations.NotNull;
|
|||
*/
|
||||
public class LocalChatCommand implements CommandExecutor {
|
||||
|
||||
private static final MiniMessage MM = MiniMessage.miniMessage();
|
||||
private final LoChat plugin;
|
||||
private final ChatService chatService;
|
||||
private static final MiniMessage MM = MiniMessage.miniMessage();
|
||||
|
||||
public LocalChatCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
|
|
@ -39,10 +39,10 @@ public class LocalChatCommand implements CommandExecutor {
|
|||
}
|
||||
|
||||
String message = String.join(" ", args);
|
||||
|
||||
|
||||
// Конвертируем String в Component для новой сигнатуры ChatService
|
||||
Component messageComponent = MM.deserialize(message);
|
||||
|
||||
|
||||
chatService.sendLocalMessage(player, messageComponent);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.loki.lochat.api.service.IgnoreService;
|
|||
import com.loki.lochat.api.service.PMService;
|
||||
import com.loki.lochat.api.service.SpyService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import com.loki.lochat.utils.PlayerUtil;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.command.Command;
|
||||
|
|
@ -89,10 +90,10 @@ public class MsgCommand implements CommandExecutor {
|
|||
|
||||
// Звук для получателя
|
||||
if (plugin.getConfigManager().isPmSoundEnabled()) {
|
||||
try {
|
||||
Sound sound = Sound.valueOf(plugin.getConfigManager().getPmSoundType());
|
||||
Sound sound = PlayerUtil.parseSound(plugin.getConfigManager().getPmSoundType(), null);
|
||||
if (sound != null) {
|
||||
target.playSound(target.getLocation(), sound, 1.0f, 1.0f);
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
// Шпион
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ public class MuteBlameCommand implements CommandExecutor, TabCompleter {
|
|||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
List<String> completions = new ArrayList<>();
|
||||
|
||||
if (args.length == 1) {
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ public class MuteCommand implements CommandExecutor, TabCompleter {
|
|||
// Получаем настройки из конфига
|
||||
String defaultReason = plugin.getConfigManager().getString("mute.default-reason", "Нету.");
|
||||
String defaultDuration = plugin.getConfigManager().getString("mute.default-duration", "7d");
|
||||
|
||||
|
||||
String reason = reasonBuilder.length() > 0 ? reasonBuilder.toString() : defaultReason;
|
||||
|
||||
// Определяем длительность
|
||||
|
|
@ -136,7 +136,7 @@ public class MuteCommand implements CommandExecutor, TabCompleter {
|
|||
// Уведомляем замученного (если онлайн)
|
||||
if (target != null) {
|
||||
String msgKey = duration == 0 ? "mute.messages.you-muted-permanent" : "mute.messages.you-muted";
|
||||
String msg = plugin.getConfigManager().getString(msgKey,
|
||||
String msg = plugin.getConfigManager().getString(msgKey,
|
||||
duration == 0 ? "§cВы замучены навсегда! Причина: %reason%" : "§cВы замучены на %duration%! Причина: %reason%");
|
||||
msg = muteService.formatMessage(msg, finalTargetName, operatorName, timeDisplay, reason);
|
||||
target.sendMessage(ChatFormatter.parse(msg));
|
||||
|
|
@ -147,7 +147,7 @@ public class MuteCommand implements CommandExecutor, TabCompleter {
|
|||
String silentMsg = plugin.getConfigManager().getString("mute.messages.silent-muted",
|
||||
"§8[Тихо] §c%player% §7замучен на §c%duration% §7(%operator%). Причина: %reason%");
|
||||
silentMsg = muteService.formatMessage(silentMsg, finalTargetName, operatorName, timeDisplay, reason);
|
||||
|
||||
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
if (p.hasPermission("lochat.mute.see-silent")) {
|
||||
p.sendMessage(ChatFormatter.parse(silentMsg));
|
||||
|
|
@ -170,7 +170,7 @@ public class MuteCommand implements CommandExecutor, TabCompleter {
|
|||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
List<String> completions = new ArrayList<>();
|
||||
|
||||
if (args.length == 1) {
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ public class MuteHistoryCommand implements CommandExecutor, TabCompleter {
|
|||
// Выводим от новых к старым
|
||||
for (int i = history.size() - 1; i >= 0; i--) {
|
||||
MuteData.MuteHistoryEntry entry = history.get(i);
|
||||
|
||||
|
||||
// Статус
|
||||
String status;
|
||||
if (entry.unmuted) {
|
||||
|
|
@ -99,13 +99,13 @@ public class MuteHistoryCommand implements CommandExecutor, TabCompleter {
|
|||
sender.sendMessage(" §7Длительность: §f" + durationStr);
|
||||
sender.sendMessage(" §7Причина: §f" + (entry.reason != null ? entry.reason : "Не указана"));
|
||||
sender.sendMessage(" §7Выдал: §f" + entry.mutedBy);
|
||||
|
||||
|
||||
if (entry.unmuted && entry.unmutedBy != null) {
|
||||
sender.sendMessage(" §7Размутил: §f" + entry.unmutedBy + " §7(" + dateFormat.format(new Date(entry.unmutedAt)) + ")");
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
|
||||
// Ограничиваем вывод
|
||||
if (index > 10) {
|
||||
sender.sendMessage("§7... и ещё " + (history.size() - 10) + " записей");
|
||||
|
|
@ -118,7 +118,7 @@ public class MuteHistoryCommand implements CommandExecutor, TabCompleter {
|
|||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
List<String> completions = new ArrayList<>();
|
||||
|
||||
if (args.length == 1) {
|
||||
|
|
|
|||
|
|
@ -45,19 +45,19 @@ public class MuteListCommand implements CommandExecutor {
|
|||
}
|
||||
|
||||
sender.sendMessage("§6§l=== Активные муты (" + activeMutes.size() + ") ===");
|
||||
|
||||
|
||||
int index = 1;
|
||||
for (Map.Entry<UUID, MuteData> entry : activeMutes.entrySet()) {
|
||||
UUID uuid = entry.getKey();
|
||||
MuteData data = entry.getValue();
|
||||
|
||||
|
||||
// Получаем имя игрока
|
||||
String playerName = data.getPlayerName();
|
||||
if (playerName == null) {
|
||||
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuid);
|
||||
playerName = offlinePlayer.getName() != null ? offlinePlayer.getName() : uuid.toString().substring(0, 8);
|
||||
}
|
||||
|
||||
|
||||
// Форматируем время
|
||||
String timeStr;
|
||||
if (data.isPermanent()) {
|
||||
|
|
@ -66,11 +66,11 @@ public class MuteListCommand implements CommandExecutor {
|
|||
long remaining = data.getEndTime() - System.currentTimeMillis();
|
||||
timeStr = "§e" + muteService.formatTime(remaining);
|
||||
}
|
||||
|
||||
|
||||
sender.sendMessage("§7" + index + ". §f" + playerName + " §7- " + timeStr);
|
||||
sender.sendMessage(" §7Причина: §f" + (data.getReason() != null ? data.getReason() : "Не указана"));
|
||||
sender.sendMessage(" §7Выдал: §f" + data.getMutedBy() + " §7(" + dateFormat.format(new Date(data.getMutedAt())) + ")");
|
||||
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,39 +17,39 @@ import java.util.List;
|
|||
public class NickCommand implements CommandExecutor, TabCompleter {
|
||||
private final LoChat plugin;
|
||||
private final NickService nickService;
|
||||
|
||||
|
||||
public NickCommand(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.nickService = plugin.getServiceRegistry().get(NickService.class);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
sender.sendMessage(Component.text("Эта команда только для игроков!", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (!player.hasPermission("lochat.nick")) {
|
||||
player.sendMessage(Component.text("У вас нет прав на использование этой команды!", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (args.length == 0) {
|
||||
return showCurrentNick(player);
|
||||
}
|
||||
|
||||
|
||||
if (args[0].equalsIgnoreCase("reset")) {
|
||||
return resetNick(player);
|
||||
}
|
||||
|
||||
|
||||
String nickname = String.join(" ", args);
|
||||
return setNick(player, nickname);
|
||||
}
|
||||
|
||||
|
||||
private boolean showCurrentNick(Player player) {
|
||||
var currentNick = nickService.getNickname(player.getUniqueId());
|
||||
|
||||
|
||||
if (currentNick.isPresent()) {
|
||||
Component nickComponent = ChatFormatter.parse(currentNick.get());
|
||||
player.sendMessage(Component.text("Ваш текущий ник: ", NamedTextColor.GRAY)
|
||||
|
|
@ -63,23 +63,23 @@ public class NickCommand implements CommandExecutor, TabCompleter {
|
|||
.append(Component.text("/nick <ник>", NamedTextColor.YELLOW))
|
||||
.append(Component.text(" для установки", NamedTextColor.GRAY)));
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private boolean resetNick(Player player) {
|
||||
var currentNick = nickService.getNickname(player.getUniqueId());
|
||||
|
||||
|
||||
if (currentNick.isEmpty()) {
|
||||
player.sendMessage(Component.text("У вас нет кастомного ника!", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
nickService.resetNickname(player.getUniqueId());
|
||||
player.sendMessage(Component.text("Ваш ник сброшен!", NamedTextColor.GREEN));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private boolean setNick(Player player, String nickname) {
|
||||
if (!nickService.isValidNickname(nickname)) {
|
||||
player.sendMessage(Component.text("Невалидный ник!", NamedTextColor.RED));
|
||||
|
|
@ -89,7 +89,7 @@ public class NickCommand implements CommandExecutor, TabCompleter {
|
|||
player.sendMessage(Component.text("• Поддержка цветов: &#FF0000текст, <gradient:&#FF0000:�FF00>текст</gradient>", NamedTextColor.GRAY));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (nickService.isNicknameTaken(nickname)) {
|
||||
var currentNick = nickService.getNickname(player.getUniqueId());
|
||||
if (currentNick.isEmpty() || !currentNick.get().equals(nickname)) {
|
||||
|
|
@ -97,9 +97,9 @@ public class NickCommand implements CommandExecutor, TabCompleter {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boolean success = nickService.setNickname(player.getUniqueId(), nickname);
|
||||
|
||||
|
||||
if (success) {
|
||||
Component nickComponent = ChatFormatter.parse(nickname);
|
||||
player.sendMessage(Component.text("Ваш ник изменён на: ", NamedTextColor.GREEN)
|
||||
|
|
@ -107,33 +107,33 @@ public class NickCommand implements CommandExecutor, TabCompleter {
|
|||
} else {
|
||||
player.sendMessage(Component.text("Не удалось установить ник!", NamedTextColor.RED));
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
List<String> completions = new ArrayList<>();
|
||||
|
||||
|
||||
if (args.length == 1) {
|
||||
String input = args[0].toLowerCase();
|
||||
|
||||
|
||||
if ("reset".startsWith(input)) {
|
||||
completions.add("reset");
|
||||
}
|
||||
|
||||
|
||||
if (sender instanceof Player player) {
|
||||
var currentNick = nickService.getNickname(player.getUniqueId());
|
||||
if (currentNick.isPresent()) {
|
||||
completions.add(currentNick.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
completions.add("&#FF0000Красный");
|
||||
completions.add("�FF00Зелёный");
|
||||
completions.add("Русский_Ник");
|
||||
}
|
||||
|
||||
|
||||
return completions;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ public class PlayerInfoCommand implements CommandExecutor {
|
|||
sender.sendMessage(ChatFormatter.parse("§eУровень: §f" + target.getLevel()));
|
||||
sender.sendMessage(ChatFormatter.parse("§eЗдоровье: §f" + Math.round(target.getHealth()) + "/20"));
|
||||
sender.sendMessage(ChatFormatter.parse("§eМир: §f" + target.getWorld().getName()));
|
||||
|
||||
|
||||
// Если есть градиентный модуль, показываем градиентное имя
|
||||
if (plugin.getGradientModule() != null) {
|
||||
String gradientName = plugin.getGradientModule().getFormattedName(target);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.loki.lochat.api.service.IgnoreService;
|
|||
import com.loki.lochat.api.service.PMService;
|
||||
import com.loki.lochat.api.service.SpyService;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import com.loki.lochat.utils.PlayerUtil;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.command.Command;
|
||||
|
|
@ -88,10 +89,10 @@ public class ReplyCommand implements CommandExecutor {
|
|||
|
||||
// Звук для получателя
|
||||
if (plugin.getConfigManager().isPmSoundEnabled()) {
|
||||
try {
|
||||
Sound sound = Sound.valueOf(plugin.getConfigManager().getPmSoundType());
|
||||
Sound sound = PlayerUtil.parseSound(plugin.getConfigManager().getPmSoundType(), null);
|
||||
if (sound != null) {
|
||||
target.playSound(target.getLocation(), sound, 1.0f, 1.0f);
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
// Шпион
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ public class UnmuteCommand implements CommandExecutor, TabCompleter {
|
|||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
List<String> completions = new ArrayList<>();
|
||||
|
||||
if (args.length == 1) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ public class ConfigManager {
|
|||
this.plugin = plugin;
|
||||
plugin.saveDefaultConfig();
|
||||
this.config = plugin.getConfig();
|
||||
|
||||
|
||||
// Проверяем версию конфига и обновляем при необходимости
|
||||
updateConfig();
|
||||
}
|
||||
|
|
@ -23,17 +23,17 @@ public class ConfigManager {
|
|||
private void updateConfig() {
|
||||
int currentVersion = config.getInt("config-version", 1);
|
||||
int requiredVersion = 2; // Текущая версия конфига
|
||||
|
||||
|
||||
if (currentVersion < requiredVersion) {
|
||||
plugin.getLogger().info("Обновление конфига с версии " + currentVersion + " до " + requiredVersion);
|
||||
|
||||
|
||||
// Добавляем новые настройки если их нет
|
||||
addMissingConfigOptions();
|
||||
|
||||
|
||||
// Обновляем версию
|
||||
config.set("config-version", requiredVersion);
|
||||
plugin.saveConfig();
|
||||
|
||||
|
||||
plugin.getLogger().info("Конфиг успешно обновлен!");
|
||||
}
|
||||
}
|
||||
|
|
@ -46,12 +46,12 @@ public class ConfigManager {
|
|||
config.set("announcements.show-actionbar", true);
|
||||
config.set("announcements.title-duration", 3);
|
||||
}
|
||||
|
||||
|
||||
// Можно добавить другие недостающие настройки
|
||||
if (!config.contains("chat.pm.enabled")) {
|
||||
config.set("chat.pm.enabled", true);
|
||||
}
|
||||
|
||||
|
||||
if (!config.contains("mentions.enabled")) {
|
||||
config.set("mentions.enabled", true);
|
||||
config.set("mentions.sound", true);
|
||||
|
|
@ -59,18 +59,18 @@ public class ConfigManager {
|
|||
config.set("mentions.highlight", "&#FFFF00@{player}");
|
||||
config.set("mentions.self-highlight", "&#FFD700{player}");
|
||||
}
|
||||
|
||||
|
||||
if (!config.contains("automessages.enabled")) {
|
||||
config.set("automessages.enabled", true);
|
||||
config.set("automessages.interval", 300);
|
||||
config.set("automessages.random", false);
|
||||
}
|
||||
|
||||
|
||||
if (!config.contains("clearchat.message.enabled")) {
|
||||
config.set("clearchat.message.enabled", true);
|
||||
config.set("clearchat.message.text", "CADFЧат очищен администратором {player}");
|
||||
}
|
||||
|
||||
|
||||
if (!config.contains("lopreff.enabled")) {
|
||||
config.set("lopreff.enabled", true);
|
||||
config.set("lopreff.use-gradient-name", true);
|
||||
|
|
@ -197,21 +197,21 @@ public class ConfigManager {
|
|||
public String getSelfMentionHighlight() {
|
||||
return ChatFormatter.convertAllColors(config.getString("mentions.self-highlight", "&#FFD700{player}"));
|
||||
}
|
||||
|
||||
|
||||
// Clear chat
|
||||
public boolean isClearChatMessageEnabled() {
|
||||
return config.getBoolean("clearchat.message.enabled", true);
|
||||
}
|
||||
|
||||
|
||||
public void setClearChatMessageEnabled(boolean enabled) {
|
||||
config.set("clearchat.message.enabled", enabled);
|
||||
plugin.saveConfig();
|
||||
}
|
||||
|
||||
|
||||
public String getClearChatMessage() {
|
||||
return config.getString("clearchat.message.text", "CADFЧат очищен администратором {player}");
|
||||
}
|
||||
|
||||
|
||||
public void setClearChatMessage(String message) {
|
||||
config.set("clearchat.message.text", message);
|
||||
plugin.saveConfig();
|
||||
|
|
|
|||
|
|
@ -8,127 +8,127 @@ import java.util.regex.Matcher;
|
|||
import java.util.regex.Pattern;
|
||||
|
||||
public class AdvancedMessageFilter {
|
||||
|
||||
|
||||
private static final Pattern URL_PATTERN = Pattern.compile(
|
||||
"(https?://)?([\\w-]+\\.)+[\\w-]+(/[\\w-./?%&=]*)?",
|
||||
Pattern.CASE_INSENSITIVE
|
||||
"(https?://)?([\\w-]+\\.)+[\\w-]+(/[\\w-./?%&=]*)?",
|
||||
Pattern.CASE_INSENSITIVE
|
||||
);
|
||||
|
||||
|
||||
private static final Pattern IP_PATTERN = Pattern.compile(
|
||||
"\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b"
|
||||
"\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b"
|
||||
);
|
||||
|
||||
|
||||
private static final Pattern REPEAT_PATTERN = Pattern.compile(
|
||||
"(.)\\1{3,}"
|
||||
"(.)\\1{3,}"
|
||||
);
|
||||
|
||||
|
||||
private final FileConfiguration config;
|
||||
private final List<String> whitelistedDomains;
|
||||
private final List<String> blacklistedDomains;
|
||||
|
||||
|
||||
public AdvancedMessageFilter(FileConfiguration config) {
|
||||
this.config = config;
|
||||
this.whitelistedDomains = config.getStringList("filters.url.whitelist");
|
||||
this.blacklistedDomains = config.getStringList("filters.url.blacklist");
|
||||
}
|
||||
|
||||
|
||||
public FilterResult filterMessage(Player player, String message) {
|
||||
if (config.getBoolean("filters.url.enabled", true)) {
|
||||
FilterResult urlResult = filterUrls(player, message);
|
||||
if (!urlResult.isAllowed()) {
|
||||
if (!urlResult.allowed()) {
|
||||
return urlResult;
|
||||
}
|
||||
message = urlResult.getFilteredMessage();
|
||||
message = urlResult.filteredMessage();
|
||||
}
|
||||
|
||||
|
||||
if (config.getBoolean("filters.ip.enabled", true)) {
|
||||
FilterResult ipResult = filterIPs(player, message);
|
||||
if (!ipResult.isAllowed()) {
|
||||
if (!ipResult.allowed()) {
|
||||
return ipResult;
|
||||
}
|
||||
message = ipResult.getFilteredMessage();
|
||||
message = ipResult.filteredMessage();
|
||||
}
|
||||
|
||||
|
||||
if (config.getBoolean("filters.repeat.enabled", true)) {
|
||||
message = filterRepeatingChars(message);
|
||||
}
|
||||
|
||||
|
||||
return new FilterResult(true, message, null);
|
||||
}
|
||||
|
||||
|
||||
private FilterResult filterUrls(Player player, String message) {
|
||||
if (player.hasPermission("lochat.bypass.urlfilter")) {
|
||||
return new FilterResult(true, message, null);
|
||||
}
|
||||
|
||||
|
||||
Matcher matcher = URL_PATTERN.matcher(message);
|
||||
|
||||
|
||||
while (matcher.find()) {
|
||||
String url = matcher.group();
|
||||
String domain = extractDomain(url);
|
||||
|
||||
|
||||
if (!whitelistedDomains.isEmpty()) {
|
||||
boolean isWhitelisted = whitelistedDomains.stream()
|
||||
.anyMatch(d -> domain.toLowerCase().contains(d.toLowerCase()));
|
||||
|
||||
.anyMatch(d -> domain.toLowerCase().contains(d.toLowerCase()));
|
||||
|
||||
if (!isWhitelisted) {
|
||||
return new FilterResult(false, message,
|
||||
config.getString("filters.url.blocked-message", "&#FF6B6BСсылки запрещены!"));
|
||||
return new FilterResult(false, message,
|
||||
config.getString("filters.url.blocked-message", "&#FF6B6BСсылки запрещены!"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!blacklistedDomains.isEmpty()) {
|
||||
boolean isBlacklisted = blacklistedDomains.stream()
|
||||
.anyMatch(d -> domain.toLowerCase().contains(d.toLowerCase()));
|
||||
|
||||
.anyMatch(d -> domain.toLowerCase().contains(d.toLowerCase()));
|
||||
|
||||
if (isBlacklisted) {
|
||||
return new FilterResult(false, message,
|
||||
config.getString("filters.url.blocked-message", "&#FF6B6BЭта ссылка запрещена!"));
|
||||
config.getString("filters.url.blocked-message", "&#FF6B6BЭта ссылка запрещена!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return new FilterResult(true, message, null);
|
||||
}
|
||||
|
||||
|
||||
private FilterResult filterIPs(Player player, String message) {
|
||||
if (player.hasPermission("lochat.bypass.ipfilter")) {
|
||||
return new FilterResult(true, message, null);
|
||||
}
|
||||
|
||||
|
||||
Matcher matcher = IP_PATTERN.matcher(message);
|
||||
|
||||
|
||||
if (matcher.find()) {
|
||||
String replacement = config.getString("filters.ip.replacement", "[IP скрыт]");
|
||||
String filtered = matcher.replaceAll(replacement);
|
||||
|
||||
|
||||
if (config.getBoolean("filters.ip.block", false)) {
|
||||
return new FilterResult(false, message,
|
||||
config.getString("filters.ip.blocked-message", "&#FF6B6BIP адреса запрещены!"));
|
||||
config.getString("filters.ip.blocked-message", "&#FF6B6BIP адреса запрещены!"));
|
||||
}
|
||||
|
||||
|
||||
return new FilterResult(true, filtered, null);
|
||||
}
|
||||
|
||||
|
||||
return new FilterResult(true, message, null);
|
||||
}
|
||||
|
||||
|
||||
private String filterRepeatingChars(String message) {
|
||||
int maxRepeats = config.getInt("filters.repeat.max-repeats", 3);
|
||||
|
||||
|
||||
Matcher matcher = REPEAT_PATTERN.matcher(message);
|
||||
StringBuffer result = new StringBuffer();
|
||||
|
||||
|
||||
while (matcher.find()) {
|
||||
String repeated = matcher.group(1);
|
||||
String replacement = repeated.repeat(Math.min(maxRepeats, matcher.group().length()));
|
||||
matcher.appendReplacement(result, Matcher.quoteReplacement(replacement));
|
||||
}
|
||||
matcher.appendTail(result);
|
||||
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
|
||||
private String extractDomain(String url) {
|
||||
url = url.replaceFirst("^https?://", "");
|
||||
int slashIndex = url.indexOf('/');
|
||||
|
|
@ -137,28 +137,7 @@ public class AdvancedMessageFilter {
|
|||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
public static class FilterResult {
|
||||
private final boolean allowed;
|
||||
private final String filteredMessage;
|
||||
private final String blockReason;
|
||||
|
||||
public FilterResult(boolean allowed, String filteredMessage, String blockReason) {
|
||||
this.allowed = allowed;
|
||||
this.filteredMessage = filteredMessage;
|
||||
this.blockReason = blockReason;
|
||||
}
|
||||
|
||||
public boolean isAllowed() {
|
||||
return allowed;
|
||||
}
|
||||
|
||||
public String getFilteredMessage() {
|
||||
return filteredMessage;
|
||||
}
|
||||
|
||||
public String getBlockReason() {
|
||||
return blockReason;
|
||||
}
|
||||
|
||||
public record FilterResult(boolean allowed, String filteredMessage, String blockReason) {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,30 +13,30 @@ import org.bukkit.plugin.java.JavaPlugin;
|
|||
public class CooldownFilter implements MessageFilter {
|
||||
private final CooldownService cooldownService;
|
||||
private final FileConfiguration config;
|
||||
|
||||
|
||||
public CooldownFilter(CooldownService cooldownService, JavaPlugin plugin) {
|
||||
this.cooldownService = cooldownService;
|
||||
this.config = plugin.getConfig();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean apply(Player player, ChatMessage message) {
|
||||
if (player.hasPermission("chat.bypass.cooldown")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
String chatType = message.getChatType();
|
||||
int cooldown = config.getInt("chat." + chatType + ".cooldown", 0);
|
||||
|
||||
|
||||
if (cooldown > 0) {
|
||||
if (cooldownService.isOnCooldown(player.getUniqueId(), chatType, cooldown)) {
|
||||
int remaining = cooldownService.getRemainingCooldown(
|
||||
player.getUniqueId(), chatType, cooldown);
|
||||
player.getUniqueId(), chatType, cooldown);
|
||||
player.sendMessage("§cПодождите " + remaining + " сек.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cooldownService.setCooldown(player.getUniqueId(), chatType);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ import org.bukkit.entity.Player;
|
|||
*/
|
||||
public class MuteFilter implements MessageFilter {
|
||||
private final MuteService muteService;
|
||||
|
||||
|
||||
public MuteFilter(MuteService muteService) {
|
||||
this.muteService = muteService;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean apply(Player player, ChatMessage message) {
|
||||
if (muteService.isMuted(player.getUniqueId())) {
|
||||
|
|
@ -23,7 +23,7 @@ public class MuteFilter implements MessageFilter {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private void sendMuteNotification(Player player) {
|
||||
long remaining = muteService.getRemainingTime(player.getUniqueId());
|
||||
String timeStr = muteService.formatTime(remaining);
|
||||
|
|
|
|||
|
|
@ -12,25 +12,23 @@ import java.util.Map;
|
|||
public class ServiceRegistry {
|
||||
private final Map<Class<?>, Object> services = new HashMap<>();
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
public ServiceRegistry(JavaPlugin plugin) {
|
||||
|
||||
private ServiceRegistry(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
init();
|
||||
}
|
||||
|
||||
public ServiceRegistry(JavaPlugin plugin, ConfigManager configManager, MessageConfig messageConfig) {
|
||||
this.plugin = plugin;
|
||||
initWithDeps(configManager, messageConfig);
|
||||
|
||||
public static ServiceRegistry create(JavaPlugin plugin) {
|
||||
ServiceRegistry registry = new ServiceRegistry(plugin);
|
||||
registry.registerServices();
|
||||
return registry;
|
||||
}
|
||||
|
||||
private void init() {
|
||||
registerServices();
|
||||
|
||||
public static ServiceRegistry createWithDeps(JavaPlugin plugin, ConfigManager configManager, MessageConfig messageConfig) {
|
||||
ServiceRegistry registry = new ServiceRegistry(plugin);
|
||||
registry.registerServicesWithDeps(configManager, messageConfig);
|
||||
return registry;
|
||||
}
|
||||
|
||||
private void initWithDeps(ConfigManager configManager, MessageConfig messageConfig) {
|
||||
registerServicesWithDeps(configManager, messageConfig);
|
||||
}
|
||||
|
||||
|
||||
private void registerServices() {
|
||||
register(ChatService.class, new ChatServiceImpl(plugin, this));
|
||||
register(MuteService.class, new MuteServiceImpl(plugin));
|
||||
|
|
@ -38,25 +36,25 @@ public class ServiceRegistry {
|
|||
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));
|
||||
register(CooldownService.class, new CooldownServiceImpl());
|
||||
register(MessageService.class, new MessageServiceImpl(plugin, this));
|
||||
register(PlayerDataService.class, new PlayerDataServiceImpl(plugin));
|
||||
|
||||
|
||||
register(PMService.class, new PMServiceImpl());
|
||||
register(IgnoreService.class, new IgnoreServiceImpl(plugin));
|
||||
register(SpyService.class, new SpyServiceImpl(plugin, messageConfig));
|
||||
register(MentionService.class, new MentionServiceImpl(configManager));
|
||||
register(NickService.class, new NickServiceImpl(plugin));
|
||||
}
|
||||
|
||||
|
||||
public <T> void register(Class<T> serviceClass, T implementation) {
|
||||
services.put(serviceClass, implementation);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(Class<T> serviceClass) {
|
||||
Object service = services.get(serviceClass);
|
||||
|
|
@ -65,7 +63,7 @@ public class ServiceRegistry {
|
|||
}
|
||||
return (T) service;
|
||||
}
|
||||
|
||||
|
||||
public void shutdown() {
|
||||
services.clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,61 +19,61 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
public class ChatServiceImpl implements ChatService {
|
||||
private final JavaPlugin plugin;
|
||||
private final Set<UUID> globalChatDisabled = ConcurrentHashMap.newKeySet();
|
||||
|
||||
|
||||
public ChatServiceImpl(JavaPlugin plugin, ServiceRegistry registry) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void sendGlobalMessage(Player sender, Object message) {
|
||||
String format = plugin.getConfig().getString("chat.global.format",
|
||||
"{prefix} {player}: {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());
|
||||
|
||||
|
||||
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));
|
||||
|
||||
.replaceText(builder -> builder.matchLiteral("{player}").replacement(playerName))
|
||||
.replaceText(builder -> builder.matchLiteral("{message}").replacement(messageComponent));
|
||||
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (!isGlobalChatDisabled(player.getUniqueId())) {
|
||||
player.sendMessage(finalComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@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());
|
||||
|
||||
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));
|
||||
|
||||
.replaceText(builder -> builder.matchLiteral("{player}").replacement(playerName))
|
||||
.replaceText(builder -> builder.matchLiteral("{message}").replacement(messageComponent));
|
||||
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (DistanceUtil.isInRange(sender, player, radius)) {
|
||||
player.sendMessage(finalComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean toggleGlobalChat(UUID player) {
|
||||
if (globalChatDisabled.contains(player)) {
|
||||
|
|
@ -84,7 +84,7 @@ public class ChatServiceImpl implements ChatService {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isGlobalChatDisabled(UUID player) {
|
||||
return globalChatDisabled.contains(player);
|
||||
|
|
|
|||
|
|
@ -12,40 +12,40 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
public class CooldownServiceImpl implements CooldownService {
|
||||
private final Map<UUID, Long> globalCooldowns = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, Long> localCooldowns = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isOnCooldown(UUID player, String type, int cooldownSeconds) {
|
||||
Map<UUID, Long> cooldowns = getCooldownMap(type);
|
||||
Long lastTime = cooldowns.get(player);
|
||||
|
||||
|
||||
if (lastTime == null) return false;
|
||||
|
||||
|
||||
long elapsed = (System.currentTimeMillis() - lastTime) / 1000;
|
||||
return elapsed < cooldownSeconds;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getRemainingCooldown(UUID player, String type, int cooldownSeconds) {
|
||||
Map<UUID, Long> cooldowns = getCooldownMap(type);
|
||||
Long lastTime = cooldowns.get(player);
|
||||
|
||||
|
||||
if (lastTime == null) return 0;
|
||||
|
||||
|
||||
long elapsed = (System.currentTimeMillis() - lastTime) / 1000;
|
||||
return Math.max(0, cooldownSeconds - (int) elapsed);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setCooldown(UUID player, String type) {
|
||||
getCooldownMap(type).put(player, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void removeCooldown(UUID player) {
|
||||
globalCooldowns.remove(player);
|
||||
localCooldowns.remove(player);
|
||||
}
|
||||
|
||||
|
||||
private Map<UUID, Long> getCooldownMap(String type) {
|
||||
return type.equals("global") ? globalCooldowns : localCooldowns;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.loki.lochat.core.service;
|
|||
|
||||
import com.loki.lochat.api.service.IgnoreService;
|
||||
import com.loki.lochat.gradient.util.FoliaUtil;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.File;
|
||||
|
|
@ -9,8 +10,6 @@ import java.io.IOException;
|
|||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
/**
|
||||
* Реализация сервиса игнорирования
|
||||
*/
|
||||
|
|
@ -39,13 +38,15 @@ public class IgnoreServiceImpl implements IgnoreService {
|
|||
for (String ignoredStr : ignoredList) {
|
||||
try {
|
||||
ignored.add(UUID.fromString(ignoredStr));
|
||||
} catch (Exception ignored2) {}
|
||||
} catch (Exception ignored2) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!ignored.isEmpty()) {
|
||||
ignoreMap.put(uuid, ignored);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.loki.lochat.core.service;
|
|||
|
||||
import com.loki.lochat.api.service.MentionService;
|
||||
import com.loki.lochat.config.ConfigManager;
|
||||
import com.loki.lochat.utils.PlayerUtil;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.entity.Player;
|
||||
|
|
@ -17,9 +18,8 @@ import java.util.regex.Pattern;
|
|||
* Реализация сервиса упоминаний
|
||||
*/
|
||||
public class MentionServiceImpl implements MentionService {
|
||||
private final ConfigManager configManager;
|
||||
private static final Pattern MENTION_PATTERN = Pattern.compile("@(\\w+)");
|
||||
|
||||
private final ConfigManager configManager;
|
||||
// Хранит для какого игрока какие части сообщения выделить
|
||||
private final Map<Player, Set<String>> highlightedNicks = new HashMap<>();
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ public class MentionServiceImpl implements MentionService {
|
|||
mentionedPlayers.add(mentioned);
|
||||
String replacement = highlight.replace("{player}", mentioned.getName());
|
||||
matcher.appendReplacement(result, replacement);
|
||||
|
||||
|
||||
// Запоминаем что этот игрок упомянут
|
||||
highlightedNicks.computeIfAbsent(mentioned, k -> new HashSet<>()).add(mentioned.getName());
|
||||
}
|
||||
|
|
@ -64,23 +64,23 @@ public class MentionServiceImpl implements MentionService {
|
|||
|
||||
String viewerName = viewer.getName();
|
||||
String result = message;
|
||||
|
||||
|
||||
// Проверяем упоминание через @ник
|
||||
String highlight = configManager.getMentionHighlight();
|
||||
String highlightedName = highlight.replace("{player}", viewerName);
|
||||
|
||||
|
||||
// Если в сообщении есть выделенный ник этого игрока — добавляем дополнительное выделение
|
||||
if (result.contains(highlightedName)) {
|
||||
// Уже выделено через processMentions, добавляем эффект
|
||||
String extraHighlight = "<bold>" + highlightedName + "</bold>";
|
||||
result = result.replace(highlightedName, extraHighlight);
|
||||
}
|
||||
|
||||
|
||||
// Также проверяем просто ник в сообщении (без @)
|
||||
// Регистронезависимый поиск
|
||||
Pattern nickPattern = Pattern.compile("(?i)\\b" + Pattern.quote(viewerName) + "\\b");
|
||||
Matcher nickMatcher = nickPattern.matcher(result);
|
||||
|
||||
|
||||
if (nickMatcher.find()) {
|
||||
// Ник найден в сообщении — выделяем для этого игрока
|
||||
String selfHighlight = configManager.getSelfMentionHighlight();
|
||||
|
|
@ -104,12 +104,7 @@ public class MentionServiceImpl implements MentionService {
|
|||
}
|
||||
|
||||
String soundName = configManager.getMentionSoundType();
|
||||
Sound sound;
|
||||
try {
|
||||
sound = Sound.valueOf(soundName);
|
||||
} catch (IllegalArgumentException e) {
|
||||
sound = Sound.BLOCK_NOTE_BLOCK_PLING;
|
||||
}
|
||||
Sound sound = PlayerUtil.parseSound(soundName, Sound.BLOCK_NOTE_BLOCK_PLING);
|
||||
|
||||
for (Player player : players) {
|
||||
player.playSound(player.getLocation(), sound, 1.0f, 1.0f);
|
||||
|
|
@ -120,13 +115,13 @@ public class MentionServiceImpl implements MentionService {
|
|||
public void notifyIfMentioned(String message, Player viewer, Player sender) {
|
||||
if (viewer.equals(sender)) return;
|
||||
if (!configManager.isMentionSoundEnabled()) return;
|
||||
|
||||
|
||||
if (isPlayerMentioned(message, viewer)) {
|
||||
String soundName = configManager.getMentionSoundType();
|
||||
try {
|
||||
Sound sound = Sound.valueOf(soundName);
|
||||
Sound sound = PlayerUtil.parseSound(soundName, null);
|
||||
if (sound != null) {
|
||||
viewer.playSound(viewer.getLocation(), sound, 1.0f, 1.0f);
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
package com.loki.lochat.core.service;
|
||||
|
||||
import com.loki.lochat.api.filter.MessageFilter;
|
||||
import com.loki.lochat.api.service.*;
|
||||
import com.loki.lochat.api.service.ChatService;
|
||||
import com.loki.lochat.api.service.CooldownService;
|
||||
import com.loki.lochat.api.service.MessageService;
|
||||
import com.loki.lochat.api.service.MuteService;
|
||||
import com.loki.lochat.core.filter.CooldownFilter;
|
||||
import com.loki.lochat.core.filter.MuteFilter;
|
||||
import com.loki.lochat.core.registry.ServiceRegistry;
|
||||
import com.loki.lochat.data.model.ChatMessage;
|
||||
import com.loki.lochat.utils.ChatFormatter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
|
|
@ -21,29 +22,29 @@ public class MessageServiceImpl implements MessageService {
|
|||
private final JavaPlugin plugin;
|
||||
private final List<MessageFilter> filters;
|
||||
private final ChatService chatService;
|
||||
|
||||
|
||||
public MessageServiceImpl(JavaPlugin plugin, ServiceRegistry registry) {
|
||||
this.plugin = plugin;
|
||||
this.chatService = registry.get(ChatService.class);
|
||||
this.filters = new ArrayList<>();
|
||||
|
||||
|
||||
// Регистрируем фильтры в порядке применения
|
||||
filters.add(new MuteFilter(registry.get(MuteService.class)));
|
||||
filters.add(new CooldownFilter(registry.get(CooldownService.class), plugin));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean processMessage(Player player, String rawMessage) {
|
||||
// Создаем объект сообщения
|
||||
ChatMessage message = ChatMessage.create(player, rawMessage);
|
||||
|
||||
|
||||
// Применяем фильтры (мут, кулдаун)
|
||||
for (MessageFilter filter : filters) {
|
||||
if (!filter.apply(player, message)) {
|
||||
return false; // Сообщение заблокировано
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Возвращаем true - сообщение разрешено
|
||||
// Рендерер сам отправит его
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public class MuteServiceImpl implements MuteService {
|
|||
private final File dataFile;
|
||||
private final File historyFile;
|
||||
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
||||
|
||||
public MuteServiceImpl(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.dataFile = new File(plugin.getDataFolder(), "mutes.json");
|
||||
|
|
@ -32,19 +32,19 @@ public class MuteServiceImpl implements MuteService {
|
|||
load();
|
||||
loadHistory();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void mute(UUID uuid, String playerName, long duration, String reason, String mutedBy) {
|
||||
long endTime = duration > 0 ? System.currentTimeMillis() + duration : 0;
|
||||
MuteData data = new MuteData(uuid, playerName, endTime, reason, mutedBy);
|
||||
mutes.put(uuid, data);
|
||||
|
||||
|
||||
// Добавляем в историю
|
||||
addToHistory(uuid, playerName, duration, reason, mutedBy);
|
||||
|
||||
|
||||
saveAsync();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean unmute(UUID uuid, String unmutedBy) {
|
||||
MuteData removed = mutes.remove(uuid);
|
||||
|
|
@ -55,12 +55,12 @@ public class MuteServiceImpl implements MuteService {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isMuted(UUID uuid) {
|
||||
MuteData data = mutes.get(uuid);
|
||||
if (data == null) return false;
|
||||
|
||||
|
||||
if (data.getEndTime() > 0 && System.currentTimeMillis() > data.getEndTime()) {
|
||||
mutes.remove(uuid);
|
||||
saveAsync();
|
||||
|
|
@ -68,13 +68,13 @@ public class MuteServiceImpl implements MuteService {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public MuteData getMuteData(UUID uuid) {
|
||||
if (!isMuted(uuid)) return null;
|
||||
return mutes.get(uuid);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public long getRemainingTime(UUID uuid) {
|
||||
MuteData data = mutes.get(uuid);
|
||||
|
|
@ -82,16 +82,16 @@ public class MuteServiceImpl implements MuteService {
|
|||
if (data.getEndTime() == 0) return -1; // Перманентный
|
||||
return Math.max(0, data.getEndTime() - System.currentTimeMillis());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String formatTime(long millis) {
|
||||
if (millis <= 0) return "0с";
|
||||
|
||||
|
||||
long seconds = millis / 1000;
|
||||
long minutes = seconds / 60;
|
||||
long hours = minutes / 60;
|
||||
long days = hours / 24;
|
||||
|
||||
|
||||
if (days > 0) {
|
||||
return days + "д " + (hours % 24) + "ч";
|
||||
} else if (hours > 0) {
|
||||
|
|
@ -102,14 +102,14 @@ public class MuteServiceImpl implements MuteService {
|
|||
return seconds + "с";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public long parseTime(String timeStr) {
|
||||
if (timeStr == null || timeStr.isEmpty()) return 0;
|
||||
|
||||
|
||||
timeStr = timeStr.toLowerCase();
|
||||
long multiplier = 1000; // базово в миллисекундах
|
||||
|
||||
|
||||
try {
|
||||
if (timeStr.endsWith("d")) {
|
||||
return Long.parseLong(timeStr.replace("d", "")) * 24 * 60 * 60 * multiplier;
|
||||
|
|
@ -126,45 +126,45 @@ public class MuteServiceImpl implements MuteService {
|
|||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public long getMaxDuration(Player player) {
|
||||
if (player.hasPermission("lochat.mute.dur.perm")) {
|
||||
return 0; // Перманентный
|
||||
}
|
||||
|
||||
|
||||
String[] durations = {"30d", "14d", "7d", "3d", "1d", "12h", "6h", "1h", "30m", "10m"};
|
||||
for (String dur : durations) {
|
||||
if (player.hasPermission("lochat.mute.dur." + dur)) {
|
||||
return parseTime(dur);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return -1; // Нет прав
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean canMuteForDuration(Player player, long duration) {
|
||||
if (player.hasPermission("lochat.mute.dur.perm")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (duration == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
long maxDuration = getMaxDuration(player);
|
||||
if (maxDuration == -1) return false;
|
||||
if (maxDuration == 0) return true;
|
||||
|
||||
|
||||
return duration <= maxDuration;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<MuteData.MuteHistoryEntry> getPlayerHistory(UUID uuid) {
|
||||
return history.getOrDefault(uuid, new ArrayList<>());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<MuteData.MuteHistoryEntry> getMutesByOperator(String operatorName) {
|
||||
List<MuteData.MuteHistoryEntry> result = new ArrayList<>();
|
||||
|
|
@ -178,7 +178,7 @@ public class MuteServiceImpl implements MuteService {
|
|||
result.sort((a, b) -> Long.compare(b.mutedAt, a.mutedAt));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public UUID getUUIDByName(String name) {
|
||||
for (Map.Entry<UUID, List<MuteData.MuteHistoryEntry>> entry : history.entrySet()) {
|
||||
|
|
@ -190,7 +190,7 @@ public class MuteServiceImpl implements MuteService {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Map<UUID, MuteData> getActiveMutes() {
|
||||
mutes.entrySet().removeIf(entry -> {
|
||||
|
|
@ -199,7 +199,7 @@ public class MuteServiceImpl implements MuteService {
|
|||
});
|
||||
return new HashMap<>(mutes);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String formatMessage(String message, String player, String operator, String duration, String reason) {
|
||||
return message
|
||||
|
|
@ -208,17 +208,17 @@ public class MuteServiceImpl implements MuteService {
|
|||
.replace("%duration%", duration != null ? duration : "")
|
||||
.replace("%reason%", reason != null ? reason : "");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
try {
|
||||
if (!dataFile.getParentFile().exists()) {
|
||||
dataFile.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
|
||||
Map<String, MuteData> toSave = new ConcurrentHashMap<>();
|
||||
mutes.forEach((uuid, data) -> toSave.put(uuid.toString(), data));
|
||||
|
||||
|
||||
try (Writer writer = new FileWriter(dataFile)) {
|
||||
gson.toJson(toSave, writer);
|
||||
}
|
||||
|
|
@ -226,52 +226,56 @@ public class MuteServiceImpl implements MuteService {
|
|||
plugin.getLogger().warning("Ошибка сохранения мутов: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void load() {
|
||||
if (!dataFile.exists()) return;
|
||||
|
||||
|
||||
try (Reader reader = new FileReader(dataFile)) {
|
||||
Type type = new TypeToken<Map<String, MuteData>>(){}.getType();
|
||||
Type type = new TypeToken<Map<String, MuteData>>() {
|
||||
}.getType();
|
||||
Map<String, MuteData> loaded = gson.fromJson(reader, type);
|
||||
if (loaded != null) {
|
||||
loaded.forEach((key, value) -> {
|
||||
try {
|
||||
mutes.put(UUID.fromString(key), value);
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("Ошибка загрузки мутов: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void loadHistory() {
|
||||
if (!historyFile.exists()) return;
|
||||
|
||||
|
||||
try (Reader reader = new FileReader(historyFile)) {
|
||||
Type type = new TypeToken<Map<String, List<MuteData.MuteHistoryEntry>>>(){}.getType();
|
||||
Type type = new TypeToken<Map<String, List<MuteData.MuteHistoryEntry>>>() {
|
||||
}.getType();
|
||||
Map<String, List<MuteData.MuteHistoryEntry>> loaded = gson.fromJson(reader, type);
|
||||
if (loaded != null) {
|
||||
loaded.forEach((key, value) -> {
|
||||
try {
|
||||
history.put(UUID.fromString(key), new ArrayList<>(value));
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("Ошибка загрузки истории мутов: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void saveHistory() {
|
||||
try {
|
||||
if (!historyFile.getParentFile().exists()) {
|
||||
historyFile.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
|
||||
Map<String, List<MuteData.MuteHistoryEntry>> toSave = new ConcurrentHashMap<>();
|
||||
history.forEach((uuid, data) -> toSave.put(uuid.toString(), data));
|
||||
|
||||
|
||||
try (Writer writer = new FileWriter(historyFile)) {
|
||||
gson.toJson(toSave, writer);
|
||||
}
|
||||
|
|
@ -279,7 +283,7 @@ public class MuteServiceImpl implements MuteService {
|
|||
plugin.getLogger().warning("Ошибка сохранения истории мутов: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void addToHistory(UUID uuid, String playerName, long duration, String reason, String mutedBy) {
|
||||
List<MuteData.MuteHistoryEntry> playerHistory = history.computeIfAbsent(uuid, k -> new ArrayList<>());
|
||||
playerHistory.add(new MuteData.MuteHistoryEntry(
|
||||
|
|
@ -294,7 +298,7 @@ public class MuteServiceImpl implements MuteService {
|
|||
));
|
||||
saveHistoryAsync();
|
||||
}
|
||||
|
||||
|
||||
private void updateHistoryUnmute(UUID uuid, String unmutedBy) {
|
||||
List<MuteData.MuteHistoryEntry> playerHistory = history.get(uuid);
|
||||
if (playerHistory != null && !playerHistory.isEmpty()) {
|
||||
|
|
@ -310,11 +314,11 @@ public class MuteServiceImpl implements MuteService {
|
|||
}
|
||||
saveHistoryAsync();
|
||||
}
|
||||
|
||||
|
||||
private void saveHistoryAsync() {
|
||||
FoliaUtil.runAsync(plugin, this::saveHistory);
|
||||
}
|
||||
|
||||
|
||||
private void saveAsync() {
|
||||
FoliaUtil.runAsync(plugin, () -> {
|
||||
save();
|
||||
|
|
|
|||
|
|
@ -12,28 +12,29 @@ import org.bukkit.plugin.java.JavaPlugin;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Реализация сервиса кастомных ников
|
||||
*/
|
||||
public class NickServiceImpl implements NickService {
|
||||
// Настройки
|
||||
private static final int MIN_LENGTH = 3;
|
||||
private static final int MAX_LENGTH = 16;
|
||||
private final JavaPlugin plugin;
|
||||
private final Map<UUID, String> nicknames = new ConcurrentHashMap<>();
|
||||
private final File dataFile;
|
||||
private FileConfiguration data;
|
||||
|
||||
// Настройки
|
||||
private static final int MIN_LENGTH = 3;
|
||||
private static final int MAX_LENGTH = 16;
|
||||
|
||||
|
||||
public NickServiceImpl(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.dataFile = new File(plugin.getDataFolder(), "nicknames.yml");
|
||||
load();
|
||||
}
|
||||
|
||||
|
||||
private void load() {
|
||||
if (!dataFile.exists()) {
|
||||
try {
|
||||
|
|
@ -43,9 +44,9 @@ public class NickServiceImpl implements NickService {
|
|||
plugin.getLogger().warning("Не удалось создать nicknames.yml: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
data = YamlConfiguration.loadConfiguration(dataFile);
|
||||
|
||||
|
||||
// Загружаем ники
|
||||
if (data.contains("nicknames")) {
|
||||
for (String key : data.getConfigurationSection("nicknames").getKeys(false)) {
|
||||
|
|
@ -54,10 +55,10 @@ public class NickServiceImpl implements NickService {
|
|||
nicknames.put(uuid, nickname);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
plugin.getLogger().info("Загружено " + nicknames.size() + " кастомных ников");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
// Сохраняем ники
|
||||
|
|
@ -65,21 +66,21 @@ public class NickServiceImpl implements NickService {
|
|||
for (Map.Entry<UUID, String> entry : nicknames.entrySet()) {
|
||||
data.set("nicknames." + entry.getKey().toString(), entry.getValue());
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
data.save(dataFile);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning("Не удалось сохранить nicknames.yml: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean setNickname(UUID player, String nickname) {
|
||||
// Валидация
|
||||
if (!isValidNickname(nickname)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Проверка уникальности
|
||||
if (isNicknameTaken(nickname)) {
|
||||
// Проверяем не свой ли это ник
|
||||
|
|
@ -88,23 +89,23 @@ public class NickServiceImpl implements NickService {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Устанавливаем ник
|
||||
nicknames.put(player, nickname);
|
||||
|
||||
|
||||
// Обновляем display
|
||||
Player onlinePlayer = Bukkit.getPlayer(player);
|
||||
if (onlinePlayer != null) {
|
||||
updatePlayerDisplay(onlinePlayer);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void resetNickname(UUID player) {
|
||||
nicknames.remove(player);
|
||||
|
||||
|
||||
// Сбрасываем display
|
||||
Player onlinePlayer = Bukkit.getPlayer(player);
|
||||
if (onlinePlayer != null) {
|
||||
|
|
@ -112,59 +113,55 @@ public class NickServiceImpl implements NickService {
|
|||
onlinePlayer.playerListName(Component.text(onlinePlayer.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Optional<String> getNickname(UUID player) {
|
||||
return Optional.ofNullable(nicknames.get(player));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isNicknameTaken(String nickname) {
|
||||
String cleanNick = stripColors(nickname).toLowerCase();
|
||||
|
||||
|
||||
// Проверяем существующие ники
|
||||
for (String existingNick : nicknames.values()) {
|
||||
if (stripColors(existingNick).equalsIgnoreCase(cleanNick)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Проверяем реальные ники онлайн игроков
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (player.getName().equalsIgnoreCase(cleanNick)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isValidNickname(String nickname) {
|
||||
if (nickname == null || nickname.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Убираем цвета для проверки длины
|
||||
String cleanNick = stripColors(nickname);
|
||||
|
||||
|
||||
// Проверка длины
|
||||
if (cleanNick.length() < MIN_LENGTH || cleanNick.length() > MAX_LENGTH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Проверка символов (разрешены буквы, цифры, _, русские буквы)
|
||||
if (!cleanNick.matches("[a-zA-Zа-яА-Я0-9_]+")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return cleanNick.matches("[a-zA-Zа-яА-Я0-9_]+");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void updatePlayerDisplay(Player player) {
|
||||
Optional<String> nickname = getNickname(player.getUniqueId());
|
||||
|
||||
|
||||
if (nickname.isPresent()) {
|
||||
// Парсим ник с цветами
|
||||
Component nickComponent = ChatFormatter.parse(nickname.get());
|
||||
|
|
@ -176,7 +173,7 @@ public class NickServiceImpl implements NickService {
|
|||
player.playerListName(Component.text(player.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Убирает цвета из строки
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -11,16 +11,16 @@ import java.util.UUID;
|
|||
*/
|
||||
public class PlayerDataServiceImpl implements PlayerDataService {
|
||||
private final CooldownService cooldownService;
|
||||
|
||||
|
||||
public PlayerDataServiceImpl(JavaPlugin plugin) {
|
||||
this.cooldownService = new CooldownServiceImpl();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void clearPlayerData(UUID player) {
|
||||
cooldownService.removeCooldown(player);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void saveAll() {
|
||||
// Сохранение всех данных при выключении
|
||||
|
|
|
|||
|
|
@ -60,10 +60,10 @@ public class SpyServiceImpl implements SpyService {
|
|||
@Override
|
||||
public void sendToSpies(Player sender, Component message, boolean isGlobal) {
|
||||
if (spyEnabled.isEmpty()) return;
|
||||
|
||||
|
||||
String chatType = isGlobal ? "Global" : "Local";
|
||||
String plainMessage = PlainTextComponentSerializer.plainText().serialize(message);
|
||||
String format = messageConfig.get("spy.chat-format",
|
||||
String format = messageConfig.get("spy.chat-format",
|
||||
"§7[SPY] §e{type} §7{sender}: §f{message}");
|
||||
|
||||
for (UUID spyUuid : spyEnabled) {
|
||||
|
|
|
|||
|
|
@ -7,33 +7,50 @@ import org.bukkit.entity.Player;
|
|||
*/
|
||||
public class ChatMessage {
|
||||
private final Player sender;
|
||||
private String content;
|
||||
private final boolean isGlobal;
|
||||
private final long timestamp;
|
||||
|
||||
private String content;
|
||||
|
||||
private ChatMessage(Player sender, String content, boolean isGlobal) {
|
||||
this.sender = sender;
|
||||
this.content = content;
|
||||
this.isGlobal = isGlobal;
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
|
||||
public static ChatMessage create(Player sender, String rawMessage) {
|
||||
// Определяем тип чата
|
||||
boolean isGlobal = rawMessage.startsWith("!") &&
|
||||
sender.hasPermission("chat.global.use");
|
||||
|
||||
String content = isGlobal ?
|
||||
rawMessage.substring(1).trim() :
|
||||
rawMessage.trim();
|
||||
|
||||
boolean isGlobal = rawMessage.startsWith("!") &&
|
||||
sender.hasPermission("chat.global.use");
|
||||
|
||||
String content = isGlobal ?
|
||||
rawMessage.substring(1).trim() :
|
||||
rawMessage.trim();
|
||||
|
||||
return new ChatMessage(sender, content, isGlobal);
|
||||
}
|
||||
|
||||
public Player getSender() { return sender; }
|
||||
public String getContent() { return content; }
|
||||
public void setContent(String content) { this.content = content; }
|
||||
public boolean isGlobal() { return isGlobal; }
|
||||
public long getTimestamp() { return timestamp; }
|
||||
public String getChatType() { return isGlobal ? "global" : "local"; }
|
||||
|
||||
public Player getSender() {
|
||||
return sender;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public boolean isGlobal() {
|
||||
return isGlobal;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public String getChatType() {
|
||||
return isGlobal ? "global" : "local";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,15 +6,16 @@ import java.util.UUID;
|
|||
* Модель данных мута (Value Object)
|
||||
*/
|
||||
public class MuteData {
|
||||
private UUID uuid;
|
||||
private String playerName;
|
||||
public long endTime; // 0 = перманентный (public for JSON serialization)
|
||||
public String reason;
|
||||
public String mutedBy;
|
||||
public long mutedAt;
|
||||
|
||||
public MuteData() {} // For JSON deserialization
|
||||
|
||||
private UUID uuid;
|
||||
private String playerName;
|
||||
|
||||
public MuteData() {
|
||||
} // For JSON deserialization
|
||||
|
||||
public MuteData(UUID uuid, String playerName, long endTime, String reason, String mutedBy) {
|
||||
this.uuid = uuid;
|
||||
this.playerName = playerName;
|
||||
|
|
@ -23,7 +24,7 @@ public class MuteData {
|
|||
this.mutedBy = mutedBy;
|
||||
this.mutedAt = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
|
||||
public MuteData(long endTime, String reason, String mutedBy, long mutedAt, String playerName) {
|
||||
this.endTime = endTime;
|
||||
this.reason = reason;
|
||||
|
|
@ -31,15 +32,35 @@ public class MuteData {
|
|||
this.mutedAt = mutedAt;
|
||||
this.playerName = playerName;
|
||||
}
|
||||
|
||||
public UUID getUuid() { return uuid; }
|
||||
public String getPlayerName() { return playerName; }
|
||||
public long getEndTime() { return endTime; }
|
||||
public String getReason() { return reason; }
|
||||
public String getMutedBy() { return mutedBy; }
|
||||
public long getMutedAt() { return mutedAt; }
|
||||
public boolean isPermanent() { return endTime == 0; }
|
||||
|
||||
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public String getPlayerName() {
|
||||
return playerName;
|
||||
}
|
||||
|
||||
public long getEndTime() {
|
||||
return endTime;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public String getMutedBy() {
|
||||
return mutedBy;
|
||||
}
|
||||
|
||||
public long getMutedAt() {
|
||||
return mutedAt;
|
||||
}
|
||||
|
||||
public boolean isPermanent() {
|
||||
return endTime == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Запись истории мута
|
||||
*/
|
||||
|
|
@ -52,12 +73,13 @@ public class MuteData {
|
|||
public boolean unmuted;
|
||||
public String unmutedBy;
|
||||
public long unmutedAt;
|
||||
|
||||
public MuteHistoryEntry() {} // For JSON deserialization
|
||||
|
||||
public MuteHistoryEntry(String playerName, long duration, String reason,
|
||||
String mutedBy, long mutedAt, boolean unmuted,
|
||||
String unmutedBy, long unmutedAt) {
|
||||
|
||||
public MuteHistoryEntry() {
|
||||
} // For JSON deserialization
|
||||
|
||||
public MuteHistoryEntry(String playerName, long duration, String reason,
|
||||
String mutedBy, long mutedAt, boolean unmuted,
|
||||
String unmutedBy, long unmutedAt) {
|
||||
this.playerName = playerName;
|
||||
this.duration = duration;
|
||||
this.reason = reason;
|
||||
|
|
|
|||
|
|
@ -37,18 +37,19 @@ public class GradientModule {
|
|||
|
||||
/**
|
||||
* Инициализация модуля
|
||||
*
|
||||
* @return true если модуль успешно инициализирован
|
||||
*/
|
||||
public boolean init() {
|
||||
// Загружаем конфиги
|
||||
config = new GradientConfig(plugin);
|
||||
messages = new GradientMessages(plugin);
|
||||
|
||||
|
||||
if (!config.isEnabled()) {
|
||||
plugin.getLogger().info("Gradient модуль отключен в конфиге.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Проверяем PlayerPoints (опционально теперь)
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("PlayerPoints")) {
|
||||
playerPointsAPI = PlayerPoints.getInstance().getAPI();
|
||||
|
|
@ -56,10 +57,10 @@ public class GradientModule {
|
|||
} else {
|
||||
plugin.getLogger().warning("Gradient: PlayerPoints не найден. Покупки будут бесплатными.");
|
||||
}
|
||||
|
||||
|
||||
// Инициализация хранилища
|
||||
dataManager = new GradientDataManager(plugin, config);
|
||||
|
||||
|
||||
// Подключение LuckPerms (опционально)
|
||||
try {
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("LuckPerms")) {
|
||||
|
|
@ -71,15 +72,15 @@ public class GradientModule {
|
|||
} catch (NoClassDefFoundError e) {
|
||||
plugin.getLogger().info("Gradient: LuckPerms не найден. Префиксы LuckPerms будут недоступны.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Регистрация команд
|
||||
registerCommands();
|
||||
|
||||
|
||||
// Регистрация слушателей
|
||||
Bukkit.getPluginManager().registerEvents(new GradientPlayerListener(this), plugin);
|
||||
Bukkit.getPluginManager().registerEvents(new GradientGUIListener(this), plugin);
|
||||
|
||||
|
||||
enabled = true;
|
||||
plugin.getLogger().info("Gradient модуль успешно загружен!");
|
||||
return true;
|
||||
|
|
@ -91,13 +92,13 @@ public class GradientModule {
|
|||
plugin.getCommand("color").setExecutor(colorCommand);
|
||||
plugin.getCommand("color").setTabCompleter(colorCommand);
|
||||
}
|
||||
|
||||
|
||||
GradientPrefixCommand prefixCommand = new GradientPrefixCommand(this);
|
||||
if (plugin.getCommand("prefix") != null) {
|
||||
plugin.getCommand("prefix").setExecutor(prefixCommand);
|
||||
plugin.getCommand("prefix").setTabCompleter(prefixCommand);
|
||||
}
|
||||
|
||||
|
||||
GradientAdminCommand adminCommand = new GradientAdminCommand(this);
|
||||
if (plugin.getCommand("aprefix") != null) {
|
||||
plugin.getCommand("aprefix").setExecutor(adminCommand);
|
||||
|
|
@ -141,22 +142,23 @@ public class GradientModule {
|
|||
|
||||
/**
|
||||
* Получает отформатированное имя игрока с градиентом и LuckPerms префиксом
|
||||
*
|
||||
* @param useTabFormat true для §x§... формата (TAB), false для &#RRGGBB
|
||||
*/
|
||||
private String getFormattedName(Player player, boolean useTabFormat) {
|
||||
if (!enabled) return player.getName();
|
||||
|
||||
|
||||
GradientPlayerData data = dataManager.getPlayerData(player.getUniqueId());
|
||||
|
||||
|
||||
// Собираем полную строку: префикс + ник
|
||||
StringBuilder fullText = new StringBuilder();
|
||||
|
||||
|
||||
// LuckPerms префикс
|
||||
String lpPrefix = null;
|
||||
if (luckPermsHook.isEnabled()) {
|
||||
lpPrefix = luckPermsHook.getActivePrefix(player);
|
||||
}
|
||||
|
||||
|
||||
// Определяем какой префикс использовать
|
||||
if (data.hasPrefix() && data.isPrefixEnabled()) {
|
||||
// Кастомный префикс
|
||||
|
|
@ -167,21 +169,21 @@ public class GradientModule {
|
|||
fullText.append(stripColors(lpPrefix));
|
||||
} else {
|
||||
// LP префикс со своими цветами, ник отдельно
|
||||
String nick = data.hasColors() && data.isColorEnabled()
|
||||
? applyGradientWithFormat(player.getName(), data.getColors(), useTabFormat)
|
||||
: player.getName();
|
||||
String nick = data.hasColors() && data.isColorEnabled()
|
||||
? applyGradientWithFormat(player.getName(), data.getColors(), useTabFormat)
|
||||
: player.getName();
|
||||
return lpPrefix + nick;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Добавляем ник
|
||||
fullText.append(player.getName());
|
||||
|
||||
|
||||
// Применяем единый градиент на всю строку
|
||||
if (data.hasColors() && data.isColorEnabled()) {
|
||||
return applyGradientWithFormat(fullText.toString(), data.getColors(), useTabFormat);
|
||||
}
|
||||
|
||||
|
||||
return fullText.toString();
|
||||
}
|
||||
|
||||
|
|
@ -211,12 +213,12 @@ public class GradientModule {
|
|||
*/
|
||||
public String getGradientNick(Player player) {
|
||||
if (!enabled) return player.getName();
|
||||
|
||||
|
||||
GradientPlayerData data = dataManager.getPlayerData(player.getUniqueId());
|
||||
if (!data.hasColors() || !data.isColorEnabled()) {
|
||||
return player.getName();
|
||||
}
|
||||
|
||||
|
||||
return GradientUtil.applyGradient(player.getName(), data.getColors(), config.isUseLegacyRgbFormat());
|
||||
}
|
||||
|
||||
|
|
@ -226,9 +228,9 @@ public class GradientModule {
|
|||
*/
|
||||
public String getPrefix(Player player) {
|
||||
if (!enabled) return "";
|
||||
|
||||
|
||||
GradientPlayerData data = dataManager.getPlayerData(player.getUniqueId());
|
||||
|
||||
|
||||
// Кастомный префикс
|
||||
if (data.hasPrefix() && data.isPrefixEnabled()) {
|
||||
String prefix = config.getPrefixFormat().replace("{prefix}", data.getPrefix());
|
||||
|
|
@ -237,7 +239,7 @@ public class GradientModule {
|
|||
}
|
||||
return prefix;
|
||||
}
|
||||
|
||||
|
||||
// LuckPerms префикс
|
||||
if (luckPermsHook.isEnabled()) {
|
||||
String lpPrefix = luckPermsHook.getActivePrefix(player);
|
||||
|
|
@ -249,7 +251,7 @@ public class GradientModule {
|
|||
return lpPrefix;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
|
|
@ -263,12 +265,35 @@ public class GradientModule {
|
|||
}
|
||||
|
||||
// Геттеры
|
||||
public JavaPlugin getPlugin() { return plugin; }
|
||||
public GradientConfig getConfig() { return config; }
|
||||
public GradientMessages getMessages() { return messages; }
|
||||
public GradientDataManager getDataManager() { return dataManager; }
|
||||
public GradientLuckPermsHook getLuckPermsHook() { return luckPermsHook; }
|
||||
public PlayerPointsAPI getPlayerPointsAPI() { return playerPointsAPI; }
|
||||
public boolean hasPlayerPoints() { return playerPointsAPI != null; }
|
||||
public boolean isEnabled() { return enabled; }
|
||||
public JavaPlugin getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
public GradientConfig getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public GradientMessages getMessages() {
|
||||
return messages;
|
||||
}
|
||||
|
||||
public GradientDataManager getDataManager() {
|
||||
return dataManager;
|
||||
}
|
||||
|
||||
public GradientLuckPermsHook getLuckPermsHook() {
|
||||
return luckPermsHook;
|
||||
}
|
||||
|
||||
public PlayerPointsAPI getPlayerPointsAPI() {
|
||||
return playerPointsAPI;
|
||||
}
|
||||
|
||||
public boolean hasPlayerPoints() {
|
||||
return playerPointsAPI != null;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,10 +26,10 @@ import java.util.List;
|
|||
*/
|
||||
public class GradientAdminCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final GradientModule module;
|
||||
private static final List<String> PRESET_COLORS = Arrays.asList(
|
||||
"#FF0000", "#FF7F00", "#FFFF00", "#00FF00", "#0000FF", "#4B0082", "#9400D3"
|
||||
);
|
||||
private final GradientModule module;
|
||||
|
||||
public GradientAdminCommand(GradientModule module) {
|
||||
this.module = module;
|
||||
|
|
@ -153,11 +153,11 @@ public class GradientAdminCommand implements CommandExecutor, TabCompleter {
|
|||
|
||||
Player onlineTarget = target.getPlayer();
|
||||
if (onlineTarget != null) {
|
||||
FoliaUtil.runEntityTask(module.getPlugin(), onlineTarget,
|
||||
FoliaUtil.runEntityTask(module.getPlugin(), onlineTarget,
|
||||
() -> DisplayNameUtil.updateDisplayName(module, onlineTarget, data));
|
||||
}
|
||||
|
||||
FoliaUtil.runAsync(module.getPlugin(),
|
||||
FoliaUtil.runAsync(module.getPlugin(),
|
||||
() -> module.getDataManager().savePlayerData(target.getUniqueId()));
|
||||
|
||||
sender.sendMessage("§aЦвет для игрока §f" + args[1] + " §aустановлен: §f" + String.join(" ", colors));
|
||||
|
|
@ -194,11 +194,11 @@ public class GradientAdminCommand implements CommandExecutor, TabCompleter {
|
|||
|
||||
Player onlineTarget = target.getPlayer();
|
||||
if (onlineTarget != null) {
|
||||
FoliaUtil.runEntityTask(module.getPlugin(), onlineTarget,
|
||||
FoliaUtil.runEntityTask(module.getPlugin(), onlineTarget,
|
||||
() -> DisplayNameUtil.updateDisplayName(module, onlineTarget, data));
|
||||
}
|
||||
|
||||
FoliaUtil.runAsync(module.getPlugin(),
|
||||
FoliaUtil.runAsync(module.getPlugin(),
|
||||
() -> module.getDataManager().savePlayerData(target.getUniqueId()));
|
||||
|
||||
sender.sendMessage("§aПрефикс для игрока §f" + args[1] + " §aустановлен: §f" + prefix);
|
||||
|
|
@ -209,7 +209,7 @@ public class GradientAdminCommand implements CommandExecutor, TabCompleter {
|
|||
sender.sendMessage("§cИспользование: /aprefix " + (enable ? "coloron" : "coloroff") + " <игрок>");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
OfflinePlayer target = Bukkit.getOfflinePlayer(args[1]);
|
||||
if (!target.hasPlayedBefore() && !target.isOnline()) {
|
||||
msg.send(sender, "player-not-found");
|
||||
|
|
@ -221,11 +221,11 @@ public class GradientAdminCommand implements CommandExecutor, TabCompleter {
|
|||
|
||||
Player onlineTarget = target.getPlayer();
|
||||
if (onlineTarget != null) {
|
||||
FoliaUtil.runEntityTask(module.getPlugin(), onlineTarget,
|
||||
FoliaUtil.runEntityTask(module.getPlugin(), onlineTarget,
|
||||
() -> DisplayNameUtil.updateDisplayName(module, onlineTarget, data));
|
||||
}
|
||||
|
||||
FoliaUtil.runAsync(module.getPlugin(),
|
||||
FoliaUtil.runAsync(module.getPlugin(),
|
||||
() -> module.getDataManager().savePlayerData(target.getUniqueId()));
|
||||
|
||||
sender.sendMessage("§aЦвет для игрока §f" + args[1] + (enable ? " §aвключён" : " §cвыключен"));
|
||||
|
|
@ -236,7 +236,7 @@ public class GradientAdminCommand implements CommandExecutor, TabCompleter {
|
|||
sender.sendMessage("§cИспользование: /aprefix " + (enable ? "prefixon" : "prefixoff") + " <игрок>");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
OfflinePlayer target = Bukkit.getOfflinePlayer(args[1]);
|
||||
if (!target.hasPlayedBefore() && !target.isOnline()) {
|
||||
msg.send(sender, "player-not-found");
|
||||
|
|
@ -256,11 +256,11 @@ public class GradientAdminCommand implements CommandExecutor, TabCompleter {
|
|||
|
||||
Player onlineTarget = target.getPlayer();
|
||||
if (onlineTarget != null) {
|
||||
FoliaUtil.runEntityTask(module.getPlugin(), onlineTarget,
|
||||
FoliaUtil.runEntityTask(module.getPlugin(), onlineTarget,
|
||||
() -> DisplayNameUtil.updateDisplayName(module, onlineTarget, data));
|
||||
}
|
||||
|
||||
FoliaUtil.runAsync(module.getPlugin(),
|
||||
FoliaUtil.runAsync(module.getPlugin(),
|
||||
() -> module.getDataManager().savePlayerData(target.getUniqueId()));
|
||||
|
||||
sender.sendMessage("§aПрефикс для игрока §f" + args[1] + (enable ? " §aвключён" : " §cвыключен"));
|
||||
|
|
@ -271,7 +271,7 @@ public class GradientAdminCommand implements CommandExecutor, TabCompleter {
|
|||
sender.sendMessage("§cИспользование: /aprefix resetcolor <игрок>");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
OfflinePlayer target = Bukkit.getOfflinePlayer(args[1]);
|
||||
if (!target.hasPlayedBefore() && !target.isOnline()) {
|
||||
msg.send(sender, "player-not-found");
|
||||
|
|
@ -284,11 +284,11 @@ public class GradientAdminCommand implements CommandExecutor, TabCompleter {
|
|||
|
||||
Player onlineTarget = target.getPlayer();
|
||||
if (onlineTarget != null) {
|
||||
FoliaUtil.runEntityTask(module.getPlugin(), onlineTarget,
|
||||
FoliaUtil.runEntityTask(module.getPlugin(), onlineTarget,
|
||||
() -> DisplayNameUtil.updateDisplayName(module, onlineTarget, data));
|
||||
}
|
||||
|
||||
FoliaUtil.runAsync(module.getPlugin(),
|
||||
FoliaUtil.runAsync(module.getPlugin(),
|
||||
() -> module.getDataManager().savePlayerData(target.getUniqueId()));
|
||||
|
||||
sender.sendMessage("§aЦвет для игрока §f" + args[1] + " §aсброшен");
|
||||
|
|
@ -299,7 +299,7 @@ public class GradientAdminCommand implements CommandExecutor, TabCompleter {
|
|||
sender.sendMessage("§cИспользование: /aprefix resetprefix <игрок>");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
OfflinePlayer target = Bukkit.getOfflinePlayer(args[1]);
|
||||
if (!target.hasPlayedBefore() && !target.isOnline()) {
|
||||
msg.send(sender, "player-not-found");
|
||||
|
|
@ -316,11 +316,11 @@ public class GradientAdminCommand implements CommandExecutor, TabCompleter {
|
|||
|
||||
Player onlineTarget = target.getPlayer();
|
||||
if (onlineTarget != null) {
|
||||
FoliaUtil.runEntityTask(module.getPlugin(), onlineTarget,
|
||||
FoliaUtil.runEntityTask(module.getPlugin(), onlineTarget,
|
||||
() -> DisplayNameUtil.updateDisplayName(module, onlineTarget, data));
|
||||
}
|
||||
|
||||
FoliaUtil.runAsync(module.getPlugin(),
|
||||
FoliaUtil.runAsync(module.getPlugin(),
|
||||
() -> module.getDataManager().savePlayerData(target.getUniqueId()));
|
||||
|
||||
sender.sendMessage("§aПрефикс для игрока §f" + args[1] + " §aсброшен");
|
||||
|
|
@ -330,7 +330,7 @@ public class GradientAdminCommand implements CommandExecutor, TabCompleter {
|
|||
int count = 0;
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
GradientPlayerData data = module.getDataManager().getPlayerData(player.getUniqueId());
|
||||
FoliaUtil.runEntityTask(module.getPlugin(), player,
|
||||
FoliaUtil.runEntityTask(module.getPlugin(), player,
|
||||
() -> DisplayNameUtil.updateDisplayName(module, player, data));
|
||||
count++;
|
||||
}
|
||||
|
|
@ -340,19 +340,19 @@ public class GradientAdminCommand implements CommandExecutor, TabCompleter {
|
|||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
if (!sender.hasPermission("gradient.admin")) return new ArrayList<>();
|
||||
|
||||
if (args.length == 1) {
|
||||
return Arrays.asList("reload", "info", "setcolor", "setprefix", "coloron", "coloroff",
|
||||
"prefixon", "prefixoff", "resetcolor", "resetprefix", "updateall").stream()
|
||||
return Arrays.asList("reload", "info", "setcolor", "setprefix", "coloron", "coloroff",
|
||||
"prefixon", "prefixoff", "resetcolor", "resetprefix", "updateall").stream()
|
||||
.filter(s -> s.startsWith(args[0].toLowerCase()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
if (args.length == 2) {
|
||||
String sub = args[0].toLowerCase();
|
||||
if (Arrays.asList("info", "setcolor", "setprefix", "coloron", "coloroff",
|
||||
if (Arrays.asList("info", "setcolor", "setprefix", "coloron", "coloroff",
|
||||
"prefixon", "prefixoff", "resetcolor", "resetprefix").contains(sub)) {
|
||||
return Bukkit.getOnlinePlayers().stream()
|
||||
.map(Player::getName)
|
||||
|
|
|
|||
|
|
@ -26,14 +26,13 @@ import java.util.function.BiFunction;
|
|||
*/
|
||||
public class GradientColorCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final GradientModule module;
|
||||
private final Map<String, BiFunction<Player, String[], Boolean>> subCommands = new HashMap<>();
|
||||
|
||||
private static final List<String> PRESET_COLORS = List.of(
|
||||
"#FF0000", "#FF7F00", "#FFFF00", "#00FF00", "#0000FF", "#4B0082", "#9400D3",
|
||||
"#FF69B4", "#00FFFF", "#FFD700", "#FFFFFF", "#000000"
|
||||
);
|
||||
private static final Set<String> SUB_COMMANDS = Set.of("on", "off", "reset", "copy");
|
||||
private final GradientModule module;
|
||||
private final Map<String, BiFunction<Player, String[], Boolean>> subCommands = new HashMap<>();
|
||||
|
||||
public GradientColorCommand(GradientModule module) {
|
||||
this.module = module;
|
||||
|
|
@ -77,12 +76,12 @@ public class GradientColorCommand implements CommandExecutor, TabCompleter {
|
|||
private boolean handleOn(Player player, String[] args) {
|
||||
GradientPlayerData data = module.getDataManager().getPlayerData(player.getUniqueId());
|
||||
GradientMessages msg = module.getMessages();
|
||||
|
||||
|
||||
if (!data.hasColors()) {
|
||||
msg.send(player, "color-no-colors");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
data.setColorEnabled(true);
|
||||
saveAndUpdate(player, data);
|
||||
msg.send(player, "color-enabled");
|
||||
|
|
@ -100,12 +99,12 @@ public class GradientColorCommand implements CommandExecutor, TabCompleter {
|
|||
private boolean handleReset(Player player, String[] args) {
|
||||
GradientPlayerData data = module.getDataManager().getPlayerData(player.getUniqueId());
|
||||
GradientMessages msg = module.getMessages();
|
||||
|
||||
|
||||
if (!data.hasColors()) {
|
||||
msg.send(player, "color-no-colors");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
data.setColors(new ArrayList<>());
|
||||
data.setColorEnabled(false);
|
||||
saveAndUpdate(player, data);
|
||||
|
|
@ -116,18 +115,18 @@ public class GradientColorCommand implements CommandExecutor, TabCompleter {
|
|||
private boolean handleCopy(Player player, String[] args) {
|
||||
GradientMessages msg = module.getMessages();
|
||||
GradientConfig cfg = module.getConfig();
|
||||
|
||||
|
||||
if (args.length < 2) {
|
||||
msg.send(player, "color-copy-usage");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Player target = Bukkit.getPlayer(args[1]);
|
||||
if (target == null) {
|
||||
msg.send(player, "player-not-found");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
GradientPlayerData targetData = module.getDataManager().getPlayerData(target.getUniqueId());
|
||||
if (!targetData.hasColors()) {
|
||||
msg.send(player, "color-copy-no-colors", "player", target.getName());
|
||||
|
|
@ -135,8 +134,8 @@ public class GradientColorCommand implements CommandExecutor, TabCompleter {
|
|||
}
|
||||
|
||||
GradientPlayerData data = module.getDataManager().getPlayerData(player.getUniqueId());
|
||||
|
||||
if (!player.hasPermission("gradient.bypass.cooldown") &&
|
||||
|
||||
if (!player.hasPermission("gradient.bypass.cooldown") &&
|
||||
!checkCooldown(player, cfg.getColorCooldown(), data.getLastColorChange())) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -148,7 +147,7 @@ public class GradientColorCommand implements CommandExecutor, TabCompleter {
|
|||
return true;
|
||||
}
|
||||
|
||||
GradientConfirmGUI gui = new GradientConfirmGUI(module, player,
|
||||
GradientConfirmGUI gui = new GradientConfirmGUI(module, player,
|
||||
GradientConfirmGUI.ConfirmType.COLOR, colors, data.getPrefix(), price);
|
||||
FoliaUtil.runEntityTask(module.getPlugin(), player, gui::open);
|
||||
return true;
|
||||
|
|
@ -170,7 +169,7 @@ public class GradientColorCommand implements CommandExecutor, TabCompleter {
|
|||
|
||||
GradientPlayerData data = module.getDataManager().getPlayerData(player.getUniqueId());
|
||||
|
||||
if (!player.hasPermission("gradient.bypass.cooldown") &&
|
||||
if (!player.hasPermission("gradient.bypass.cooldown") &&
|
||||
!checkCooldown(player, cfg.getColorCooldown(), data.getLastColorChange())) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -191,7 +190,7 @@ public class GradientColorCommand implements CommandExecutor, TabCompleter {
|
|||
return true;
|
||||
}
|
||||
|
||||
GradientConfirmGUI gui = new GradientConfirmGUI(module, player,
|
||||
GradientConfirmGUI gui = new GradientConfirmGUI(module, player,
|
||||
GradientConfirmGUI.ConfirmType.COLOR, colors, data.getPrefix(), price);
|
||||
FoliaUtil.runEntityTask(module.getPlugin(), player, gui::open);
|
||||
return true;
|
||||
|
|
@ -222,35 +221,35 @@ public class GradientColorCommand implements CommandExecutor, TabCompleter {
|
|||
}
|
||||
|
||||
private void saveAndUpdate(Player player, GradientPlayerData data) {
|
||||
FoliaUtil.runEntityTask(module.getPlugin(), player,
|
||||
FoliaUtil.runEntityTask(module.getPlugin(), player,
|
||||
() -> DisplayNameUtil.updateDisplayName(module, player, data));
|
||||
FoliaUtil.runAsync(module.getPlugin(),
|
||||
FoliaUtil.runAsync(module.getPlugin(),
|
||||
() -> module.getDataManager().savePlayerData(player.getUniqueId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
if (args.length == 1) {
|
||||
String input = args[0].toLowerCase();
|
||||
List<String> completions = new ArrayList<>(SUB_COMMANDS);
|
||||
completions.addAll(PRESET_COLORS);
|
||||
return completions.stream().filter(s -> s.toLowerCase().startsWith(input)).toList();
|
||||
}
|
||||
|
||||
|
||||
String firstArg = args[0].toLowerCase();
|
||||
|
||||
|
||||
if (args.length == 2 && firstArg.equals("copy")) {
|
||||
return Bukkit.getOnlinePlayers().stream().map(Player::getName)
|
||||
.filter(s -> s.toLowerCase().startsWith(args[1].toLowerCase())).toList();
|
||||
}
|
||||
|
||||
|
||||
if (!SUB_COMMANDS.contains(firstArg) && args.length <= module.getConfig().getMaxColors()) {
|
||||
return PRESET_COLORS.stream()
|
||||
.filter(s -> s.toLowerCase().startsWith(args[args.length - 1].toLowerCase()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,13 +28,12 @@ import java.util.regex.Pattern;
|
|||
*/
|
||||
public class GradientPrefixCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final GradientModule module;
|
||||
private final Map<String, BiFunction<Player, String[], Boolean>> subCommands = new HashMap<>();
|
||||
|
||||
private static final Pattern COLOR_CODE_PATTERN = Pattern.compile(
|
||||
"(?i)(&[0-9a-fk-or]|§[0-9a-fk-or]|#[0-9a-f]{6}|&#[0-9a-f]{6}|<#[0-9a-f]{6}>)");
|
||||
private static final Pattern ALLOWED_PREFIX_PATTERN = Pattern.compile("^[a-zA-Zа-яА-ЯёЁ0-9 _\\-]+$");
|
||||
private static final Set<String> SUB_COMMANDS = Set.of("on", "off", "reset");
|
||||
private final GradientModule module;
|
||||
private final Map<String, BiFunction<Player, String[], Boolean>> subCommands = new HashMap<>();
|
||||
|
||||
public GradientPrefixCommand(GradientModule module) {
|
||||
this.module = module;
|
||||
|
|
@ -77,12 +76,12 @@ public class GradientPrefixCommand implements CommandExecutor, TabCompleter {
|
|||
private boolean handleOn(Player player, String[] args) {
|
||||
GradientPlayerData data = module.getDataManager().getPlayerData(player.getUniqueId());
|
||||
GradientMessages msg = module.getMessages();
|
||||
|
||||
|
||||
if (!data.hasPrefix()) {
|
||||
msg.send(player, "prefix-no-prefix");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
data.setPrefixEnabled(true);
|
||||
if (module.getLuckPermsHook() != null) {
|
||||
module.getLuckPermsHook().setPrefix(player, DisplayNameUtil.buildColoredPrefix(module, data));
|
||||
|
|
@ -106,12 +105,12 @@ public class GradientPrefixCommand implements CommandExecutor, TabCompleter {
|
|||
private boolean handleReset(Player player, String[] args) {
|
||||
GradientPlayerData data = module.getDataManager().getPlayerData(player.getUniqueId());
|
||||
GradientMessages msg = module.getMessages();
|
||||
|
||||
|
||||
if (!data.hasPrefix()) {
|
||||
msg.send(player, "prefix-no-prefix");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
data.setPrefix(null);
|
||||
data.setPrefixEnabled(false);
|
||||
if (module.getLuckPermsHook() != null) {
|
||||
|
|
@ -154,7 +153,7 @@ public class GradientPrefixCommand implements CommandExecutor, TabCompleter {
|
|||
|
||||
GradientPlayerData data = module.getDataManager().getPlayerData(player.getUniqueId());
|
||||
|
||||
if (!player.hasPermission("gradient.bypass.cooldown") &&
|
||||
if (!player.hasPermission("gradient.bypass.cooldown") &&
|
||||
!checkCooldown(player, cfg.getPrefixCooldown(), data.getLastPrefixChange())) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -165,9 +164,9 @@ public class GradientPrefixCommand implements CommandExecutor, TabCompleter {
|
|||
return true;
|
||||
}
|
||||
|
||||
GradientConfirmGUI gui = new GradientConfirmGUI(module, player,
|
||||
GradientConfirmGUI.ConfirmType.PREFIX,
|
||||
data.hasColors() && data.isColorEnabled() ? data.getColors() : null,
|
||||
GradientConfirmGUI gui = new GradientConfirmGUI(module, player,
|
||||
GradientConfirmGUI.ConfirmType.PREFIX,
|
||||
data.hasColors() && data.isColorEnabled() ? data.getColors() : null,
|
||||
prefix, price);
|
||||
FoliaUtil.runEntityTask(module.getPlugin(), player, gui::open);
|
||||
return true;
|
||||
|
|
@ -203,15 +202,15 @@ public class GradientPrefixCommand implements CommandExecutor, TabCompleter {
|
|||
}
|
||||
|
||||
private void saveAndUpdate(Player player, GradientPlayerData data) {
|
||||
FoliaUtil.runEntityTask(module.getPlugin(), player,
|
||||
FoliaUtil.runEntityTask(module.getPlugin(), player,
|
||||
() -> DisplayNameUtil.updateDisplayName(module, player, data));
|
||||
FoliaUtil.runAsync(module.getPlugin(),
|
||||
FoliaUtil.runAsync(module.getPlugin(),
|
||||
() -> module.getDataManager().savePlayerData(player.getUniqueId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
@NotNull String alias, @NotNull String[] args) {
|
||||
if (args.length == 1) {
|
||||
return SUB_COMMANDS.stream()
|
||||
.filter(s -> s.startsWith(args[0].toLowerCase()))
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public class GradientConfig {
|
|||
plugin.saveResource("gradient-config.yml", false);
|
||||
}
|
||||
config = YamlConfiguration.loadConfiguration(configFile);
|
||||
|
||||
|
||||
// Загружаем дефолты
|
||||
InputStream defStream = plugin.getResource("gradient-config.yml");
|
||||
if (defStream != null) {
|
||||
|
|
@ -56,23 +56,70 @@ public class GradientConfig {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean isEnabled() { return config.getBoolean("enabled", true); }
|
||||
public int getMaxColors() { return config.getInt("max-colors", 7); }
|
||||
public int getMinColors() { return config.getInt("min-colors", 1); }
|
||||
public int getPricePerColor() { return config.getInt("price-per-color", 50); }
|
||||
public int getPrefixPrice() { return config.getInt("prefix-price", 500); }
|
||||
public boolean isPrefixOneTimePurchase() { return config.getBoolean("prefix-one-time-purchase", true); }
|
||||
public int getColorCooldown() { return config.getInt("color-cooldown", 60); }
|
||||
public int getPrefixCooldown() { return config.getInt("prefix-cooldown", 300); }
|
||||
public int getMaxPrefixLength() { return config.getInt("max-prefix-length", 7); }
|
||||
public String getPrefixFormat() { return config.getString("prefix-format", "[{prefix}] "); }
|
||||
public boolean isGradientOnPrefix() { return config.getBoolean("gradient-on-prefix", true); }
|
||||
public boolean isGradientOnLuckPermsPrefix() { return config.getBoolean("gradient-on-luckperms-prefix", true); }
|
||||
public boolean isContinuousGradient() { return config.getBoolean("continuous-gradient", true); }
|
||||
public boolean isUseLegacyRgbFormat() { return config.getBoolean("use-legacy-rgb-format", true); }
|
||||
public String getStorageType() { return config.getString("storage-type", "YAML"); }
|
||||
public String getSqliteFile() { return config.getString("sqlite-file", "gradient-data.db"); }
|
||||
|
||||
public boolean isEnabled() {
|
||||
return config.getBoolean("enabled", true);
|
||||
}
|
||||
|
||||
public int getMaxColors() {
|
||||
return config.getInt("max-colors", 7);
|
||||
}
|
||||
|
||||
public int getMinColors() {
|
||||
return config.getInt("min-colors", 1);
|
||||
}
|
||||
|
||||
public int getPricePerColor() {
|
||||
return config.getInt("price-per-color", 50);
|
||||
}
|
||||
|
||||
public int getPrefixPrice() {
|
||||
return config.getInt("prefix-price", 500);
|
||||
}
|
||||
|
||||
public boolean isPrefixOneTimePurchase() {
|
||||
return config.getBoolean("prefix-one-time-purchase", true);
|
||||
}
|
||||
|
||||
public int getColorCooldown() {
|
||||
return config.getInt("color-cooldown", 60);
|
||||
}
|
||||
|
||||
public int getPrefixCooldown() {
|
||||
return config.getInt("prefix-cooldown", 300);
|
||||
}
|
||||
|
||||
public int getMaxPrefixLength() {
|
||||
return config.getInt("max-prefix-length", 7);
|
||||
}
|
||||
|
||||
public String getPrefixFormat() {
|
||||
return config.getString("prefix-format", "[{prefix}] ");
|
||||
}
|
||||
|
||||
public boolean isGradientOnPrefix() {
|
||||
return config.getBoolean("gradient-on-prefix", true);
|
||||
}
|
||||
|
||||
public boolean isGradientOnLuckPermsPrefix() {
|
||||
return config.getBoolean("gradient-on-luckperms-prefix", true);
|
||||
}
|
||||
|
||||
public boolean isContinuousGradient() {
|
||||
return config.getBoolean("continuous-gradient", true);
|
||||
}
|
||||
|
||||
public boolean isUseLegacyRgbFormat() {
|
||||
return config.getBoolean("use-legacy-rgb-format", true);
|
||||
}
|
||||
|
||||
public String getStorageType() {
|
||||
return config.getString("storage-type", "YAML");
|
||||
}
|
||||
|
||||
public String getSqliteFile() {
|
||||
return config.getString("sqlite-file", "gradient-data.db");
|
||||
}
|
||||
|
||||
public List<String> getPrefixBlacklist() {
|
||||
return config.getStringList("prefix-blacklist");
|
||||
}
|
||||
|
|
@ -83,17 +130,17 @@ public class GradientConfig {
|
|||
.anyMatch(blacklisted -> lowerPrefix.contains(blacklisted.toLowerCase()));
|
||||
}
|
||||
|
||||
public boolean isUpdateDisplayName() {
|
||||
public boolean isUpdateDisplayName() {
|
||||
if (mainConfig.contains("display.update-display-name")) {
|
||||
return mainConfig.getBoolean("display.update-display-name", true);
|
||||
}
|
||||
return config.getBoolean("display.update-display-name", true);
|
||||
return config.getBoolean("display.update-display-name", true);
|
||||
}
|
||||
|
||||
public boolean isUpdateTabList() {
|
||||
|
||||
public boolean isUpdateTabList() {
|
||||
if (mainConfig.contains("display.update-tab-list")) {
|
||||
return mainConfig.getBoolean("display.update-tab-list", true);
|
||||
}
|
||||
return config.getBoolean("display.update-tab-list", true);
|
||||
return config.getBoolean("display.update-tab-list", true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ public class GradientMessages {
|
|||
plugin.saveResource("gradient-messages.yml", false);
|
||||
}
|
||||
messages = YamlConfiguration.loadConfiguration(messagesFile);
|
||||
|
||||
|
||||
InputStream defStream = plugin.getResource("gradient-messages.yml");
|
||||
if (defStream != null) {
|
||||
YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public class GradientDataManager {
|
|||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
this.useSqlite = config.getStorageType().equalsIgnoreCase("SQLITE");
|
||||
|
||||
|
||||
if (useSqlite) {
|
||||
initSqlite();
|
||||
} else {
|
||||
|
|
@ -40,20 +40,20 @@ public class GradientDataManager {
|
|||
try {
|
||||
File dbFile = new File(plugin.getDataFolder(), config.getSqliteFile());
|
||||
sqliteConnection = DriverManager.getConnection("jdbc:sqlite:" + dbFile.getAbsolutePath());
|
||||
|
||||
|
||||
try (Statement stmt = sqliteConnection.createStatement()) {
|
||||
stmt.executeUpdate("""
|
||||
CREATE TABLE IF NOT EXISTS gradient_data (
|
||||
uuid TEXT PRIMARY KEY,
|
||||
prefix TEXT,
|
||||
colors TEXT,
|
||||
color_enabled INTEGER DEFAULT 1,
|
||||
prefix_enabled INTEGER DEFAULT 1,
|
||||
prefix_purchased INTEGER DEFAULT 0,
|
||||
last_color_change INTEGER DEFAULT 0,
|
||||
last_prefix_change INTEGER DEFAULT 0
|
||||
)
|
||||
""");
|
||||
CREATE TABLE IF NOT EXISTS gradient_data (
|
||||
uuid TEXT PRIMARY KEY,
|
||||
prefix TEXT,
|
||||
colors TEXT,
|
||||
color_enabled INTEGER DEFAULT 1,
|
||||
prefix_enabled INTEGER DEFAULT 1,
|
||||
prefix_purchased INTEGER DEFAULT 0,
|
||||
last_color_change INTEGER DEFAULT 0,
|
||||
last_prefix_change INTEGER DEFAULT 0
|
||||
)
|
||||
""");
|
||||
}
|
||||
plugin.getLogger().info("Gradient SQLite база данных инициализирована.");
|
||||
} catch (SQLException e) {
|
||||
|
|
@ -65,7 +65,7 @@ public class GradientDataManager {
|
|||
private void loadYamlData() {
|
||||
File dataFile = new File(plugin.getDataFolder(), "gradient-data.yml");
|
||||
if (!dataFile.exists()) return;
|
||||
|
||||
|
||||
FileConfiguration data = YamlConfiguration.loadConfiguration(dataFile);
|
||||
for (String uuidStr : data.getKeys(false)) {
|
||||
try {
|
||||
|
|
@ -79,7 +79,8 @@ public class GradientDataManager {
|
|||
playerData.setLastColorChange(data.getLong(uuidStr + ".last-color-change", 0));
|
||||
playerData.setLastPrefixChange(data.getLong(uuidStr + ".last-prefix-change", 0));
|
||||
cache.put(uuid, playerData);
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -89,7 +90,7 @@ public class GradientDataManager {
|
|||
|
||||
private GradientPlayerData loadPlayerData(UUID uuid) {
|
||||
GradientPlayerData data = new GradientPlayerData(uuid);
|
||||
|
||||
|
||||
if (useSqlite && sqliteConnection != null) {
|
||||
try (PreparedStatement stmt = sqliteConnection.prepareStatement(
|
||||
"SELECT prefix, colors, color_enabled, prefix_enabled, prefix_purchased, last_color_change, last_prefix_change FROM gradient_data WHERE uuid = ?")) {
|
||||
|
|
@ -111,14 +112,14 @@ public class GradientDataManager {
|
|||
plugin.getLogger().warning("Ошибка загрузки gradient данных: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public void savePlayerData(UUID uuid) {
|
||||
GradientPlayerData data = cache.get(uuid);
|
||||
if (data == null) return;
|
||||
|
||||
|
||||
if (useSqlite && sqliteConnection != null) {
|
||||
try (PreparedStatement stmt = sqliteConnection.prepareStatement(
|
||||
"INSERT OR REPLACE INTO gradient_data (uuid, prefix, colors, color_enabled, prefix_enabled, prefix_purchased, last_color_change, last_prefix_change) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")) {
|
||||
|
|
@ -150,7 +151,7 @@ public class GradientDataManager {
|
|||
private void saveYamlData() {
|
||||
File dataFile = new File(plugin.getDataFolder(), "gradient-data.yml");
|
||||
FileConfiguration data = new YamlConfiguration();
|
||||
|
||||
|
||||
for (Map.Entry<UUID, GradientPlayerData> entry : cache.entrySet()) {
|
||||
String path = entry.getKey().toString();
|
||||
GradientPlayerData pd = entry.getValue();
|
||||
|
|
@ -162,7 +163,7 @@ public class GradientDataManager {
|
|||
data.set(path + ".last-color-change", pd.getLastColorChange());
|
||||
data.set(path + ".last-prefix-change", pd.getLastPrefixChange());
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
data.save(dataFile);
|
||||
} catch (IOException e) {
|
||||
|
|
|
|||
|
|
@ -29,25 +29,71 @@ public class GradientPlayerData {
|
|||
this.lastPrefixChange = 0;
|
||||
}
|
||||
|
||||
public UUID getUuid() { return uuid; }
|
||||
public String getPrefix() { return prefix; }
|
||||
public void setPrefix(String prefix) { this.prefix = prefix; }
|
||||
public List<String> getColors() { return colors; }
|
||||
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public void setPrefix(String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public List<String> getColors() {
|
||||
return colors;
|
||||
}
|
||||
|
||||
public void setColors(List<String> colors) {
|
||||
this.colors = colors != null ? new ArrayList<>(colors) : new ArrayList<>();
|
||||
}
|
||||
|
||||
public boolean hasPrefix() { return prefix != null && !prefix.isEmpty(); }
|
||||
public boolean hasColors() { return colors != null && !colors.isEmpty(); }
|
||||
public boolean isColorEnabled() { return colorEnabled; }
|
||||
public void setColorEnabled(boolean colorEnabled) { this.colorEnabled = colorEnabled; }
|
||||
public boolean isPrefixEnabled() { return prefixEnabled; }
|
||||
public void setPrefixEnabled(boolean prefixEnabled) { this.prefixEnabled = prefixEnabled; }
|
||||
public boolean isPrefixPurchased() { return prefixPurchased; }
|
||||
public void setPrefixPurchased(boolean prefixPurchased) { this.prefixPurchased = prefixPurchased; }
|
||||
public long getLastColorChange() { return lastColorChange; }
|
||||
public void setLastColorChange(long lastColorChange) { this.lastColorChange = lastColorChange; }
|
||||
public long getLastPrefixChange() { return lastPrefixChange; }
|
||||
public void setLastPrefixChange(long lastPrefixChange) { this.lastPrefixChange = lastPrefixChange; }
|
||||
public boolean hasPrefix() {
|
||||
return prefix != null && !prefix.isEmpty();
|
||||
}
|
||||
|
||||
public boolean hasColors() {
|
||||
return colors != null && !colors.isEmpty();
|
||||
}
|
||||
|
||||
public boolean isColorEnabled() {
|
||||
return colorEnabled;
|
||||
}
|
||||
|
||||
public void setColorEnabled(boolean colorEnabled) {
|
||||
this.colorEnabled = colorEnabled;
|
||||
}
|
||||
|
||||
public boolean isPrefixEnabled() {
|
||||
return prefixEnabled;
|
||||
}
|
||||
|
||||
public void setPrefixEnabled(boolean prefixEnabled) {
|
||||
this.prefixEnabled = prefixEnabled;
|
||||
}
|
||||
|
||||
public boolean isPrefixPurchased() {
|
||||
return prefixPurchased;
|
||||
}
|
||||
|
||||
public void setPrefixPurchased(boolean prefixPurchased) {
|
||||
this.prefixPurchased = prefixPurchased;
|
||||
}
|
||||
|
||||
public long getLastColorChange() {
|
||||
return lastColorChange;
|
||||
}
|
||||
|
||||
public void setLastColorChange(long lastColorChange) {
|
||||
this.lastColorChange = lastColorChange;
|
||||
}
|
||||
|
||||
public long getLastPrefixChange() {
|
||||
return lastPrefixChange;
|
||||
}
|
||||
|
||||
public void setLastPrefixChange(long lastPrefixChange) {
|
||||
this.lastPrefixChange = lastPrefixChange;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,27 +23,22 @@ import java.util.List;
|
|||
/**
|
||||
* GUI подтверждения для градиентного модуля
|
||||
*/
|
||||
public class GradientConfirmGUI implements InventoryHolder {
|
||||
|
||||
public enum ConfirmType {
|
||||
COLOR, PREFIX
|
||||
}
|
||||
public final class GradientConfirmGUI implements InventoryHolder {
|
||||
|
||||
private static final LegacyComponentSerializer SERIALIZER = LegacyComponentSerializer.builder()
|
||||
.character('&')
|
||||
.hexCharacter('#')
|
||||
.hexColors()
|
||||
.build();
|
||||
|
||||
private final GradientModule module;
|
||||
private final Player player;
|
||||
private final Inventory inventory;
|
||||
private Inventory inventory;
|
||||
private final ConfirmType type;
|
||||
private final List<String> colors;
|
||||
private final String prefix;
|
||||
private final int price;
|
||||
|
||||
public GradientConfirmGUI(GradientModule module, Player player, ConfirmType type,
|
||||
public GradientConfirmGUI(GradientModule module, Player player, ConfirmType type,
|
||||
List<String> colors, String prefix, int price) {
|
||||
this.module = module;
|
||||
this.player = player;
|
||||
|
|
@ -51,7 +46,11 @@ public class GradientConfirmGUI implements InventoryHolder {
|
|||
this.colors = colors;
|
||||
this.prefix = prefix;
|
||||
this.price = price;
|
||||
this.inventory = Bukkit.createInventory(this, 27,
|
||||
}
|
||||
|
||||
private void initInventory() {
|
||||
if (inventory != null) return;
|
||||
this.inventory = Bukkit.createInventory(this, 27,
|
||||
Component.text("Подтверждение", NamedTextColor.DARK_GRAY));
|
||||
setupItems();
|
||||
}
|
||||
|
|
@ -65,12 +64,12 @@ public class GradientConfirmGUI implements InventoryHolder {
|
|||
ItemStack head = new ItemStack(Material.PLAYER_HEAD);
|
||||
SkullMeta skullMeta = (SkullMeta) head.getItemMeta();
|
||||
skullMeta.setOwningPlayer(player);
|
||||
|
||||
|
||||
String preview = buildPreview();
|
||||
// Для GUI используем LegacyComponentSerializer с поддержкой HEX
|
||||
Component previewComponent = SERIALIZER.deserialize(preview);
|
||||
skullMeta.displayName(previewComponent.decoration(TextDecoration.ITALIC, false));
|
||||
|
||||
|
||||
List<Component> lore = new ArrayList<>();
|
||||
lore.add(Component.empty());
|
||||
lore.add(Component.text("Так будет выглядеть ваш ник", NamedTextColor.GRAY)
|
||||
|
|
@ -134,7 +133,7 @@ public class GradientConfirmGUI implements InventoryHolder {
|
|||
if (lpPrefix != null && !lpPrefix.isEmpty()) {
|
||||
// Конвертируем LP префикс в legacy формат
|
||||
lpPrefix = convertToLegacy(lpPrefix);
|
||||
|
||||
|
||||
// Если градиент на LP префикс включен - применяем градиент на всю строку
|
||||
if (colorsToUse != null && !colorsToUse.isEmpty() && module.getConfig().isGradientOnLuckPermsPrefix()) {
|
||||
String cleanLpPrefix = stripColors(lpPrefix);
|
||||
|
|
@ -187,14 +186,37 @@ public class GradientConfirmGUI implements InventoryHolder {
|
|||
}
|
||||
|
||||
public void open() {
|
||||
initInventory();
|
||||
player.openInventory(inventory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Inventory getInventory() { return inventory; }
|
||||
public Player getPlayer() { return player; }
|
||||
public ConfirmType getType() { return type; }
|
||||
public List<String> getColors() { return colors; }
|
||||
public String getPrefix() { return prefix; }
|
||||
public int getPrice() { return price; }
|
||||
public @NotNull Inventory getInventory() {
|
||||
initInventory();
|
||||
return inventory;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public ConfirmType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public List<String> getColors() {
|
||||
return colors;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public int getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public enum ConfirmType {
|
||||
COLOR, PREFIX
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public class GradientLuckPermsHook {
|
|||
|
||||
public GradientLuckPermsHook(JavaPlugin plugin) {
|
||||
this.enabled = false;
|
||||
|
||||
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("LuckPerms")) {
|
||||
try {
|
||||
this.luckPerms = LuckPermsProvider.get();
|
||||
|
|
@ -40,12 +40,12 @@ public class GradientLuckPermsHook {
|
|||
|
||||
public void setPrefix(Player player, String prefix) {
|
||||
if (!isEnabled()) return;
|
||||
|
||||
|
||||
User user = luckPerms.getUserManager().getUser(player.getUniqueId());
|
||||
if (user == null) return;
|
||||
|
||||
user.data().clear(node ->
|
||||
node instanceof PrefixNode && ((PrefixNode) node).getPriority() == GradientConstants.LUCKPERMS_PREFIX_PRIORITY
|
||||
user.data().clear(node ->
|
||||
node instanceof PrefixNode && ((PrefixNode) node).getPriority() == GradientConstants.LUCKPERMS_PREFIX_PRIORITY
|
||||
);
|
||||
|
||||
if (prefix != null && !prefix.isEmpty()) {
|
||||
|
|
@ -58,12 +58,12 @@ public class GradientLuckPermsHook {
|
|||
|
||||
public void setPrefix(UUID uuid, String prefix) {
|
||||
if (!isEnabled()) return;
|
||||
|
||||
|
||||
luckPerms.getUserManager().loadUser(uuid).thenAccept(user -> {
|
||||
if (user == null) return;
|
||||
|
||||
user.data().clear(node ->
|
||||
node instanceof PrefixNode && ((PrefixNode) node).getPriority() == GradientConstants.LUCKPERMS_PREFIX_PRIORITY
|
||||
user.data().clear(node ->
|
||||
node instanceof PrefixNode && ((PrefixNode) node).getPriority() == GradientConstants.LUCKPERMS_PREFIX_PRIORITY
|
||||
);
|
||||
|
||||
if (prefix != null && !prefix.isEmpty()) {
|
||||
|
|
@ -85,10 +85,10 @@ public class GradientLuckPermsHook {
|
|||
|
||||
public String getActivePrefix(Player player) {
|
||||
if (!isEnabled()) return null;
|
||||
|
||||
|
||||
User user = luckPerms.getUserManager().getUser(player.getUniqueId());
|
||||
if (user == null) return null;
|
||||
|
||||
|
||||
CachedMetaData metaData = user.getCachedData().getMetaData();
|
||||
return metaData.getPrefix();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,12 +19,11 @@ import java.util.Map;
|
|||
*/
|
||||
public class GradientGUIListener implements Listener {
|
||||
|
||||
private final GradientModule module;
|
||||
|
||||
private static final Map<GradientConfirmGUI.ConfirmType, String> PRICE_MSG_KEYS = Map.of(
|
||||
GradientConfirmGUI.ConfirmType.COLOR, "not-enough-points-color",
|
||||
GradientConfirmGUI.ConfirmType.PREFIX, "not-enough-points-prefix"
|
||||
);
|
||||
private final GradientModule module;
|
||||
|
||||
public GradientGUIListener(GradientModule module) {
|
||||
this.module = module;
|
||||
|
|
@ -33,9 +32,9 @@ public class GradientGUIListener implements Listener {
|
|||
@EventHandler
|
||||
public void onInventoryClick(InventoryClickEvent event) {
|
||||
if (!(event.getInventory().getHolder() instanceof GradientConfirmGUI gui)) return;
|
||||
|
||||
|
||||
event.setCancelled(true);
|
||||
|
||||
|
||||
if (!(event.getWhoClicked() instanceof Player player) || !player.equals(gui.getPlayer())) return;
|
||||
|
||||
switch (event.getRawSlot()) {
|
||||
|
|
@ -46,10 +45,10 @@ public class GradientGUIListener implements Listener {
|
|||
|
||||
private void handleConfirm(Player player, GradientConfirmGUI gui) {
|
||||
player.closeInventory();
|
||||
|
||||
|
||||
GradientMessages msg = module.getMessages();
|
||||
int price = gui.getPrice();
|
||||
|
||||
|
||||
if (price > 0 && !player.hasPermission("gradient.bypass.cost")) {
|
||||
if (!module.hasPlayerPoints()) {
|
||||
player.sendMessage("§cPlayerPoints не установлен!");
|
||||
|
|
@ -85,14 +84,14 @@ public class GradientGUIListener implements Listener {
|
|||
if (module.getLuckPermsHook() != null) {
|
||||
module.getLuckPermsHook().setPrefix(player, DisplayNameUtil.buildColoredPrefix(module, data));
|
||||
}
|
||||
msg.send(player, price > 0 ? "prefix-success" : "prefix-success-free",
|
||||
msg.send(player, price > 0 ? "prefix-success" : "prefix-success-free",
|
||||
"price", String.valueOf(price));
|
||||
}
|
||||
}
|
||||
|
||||
FoliaUtil.runEntityTask(module.getPlugin(), player,
|
||||
FoliaUtil.runEntityTask(module.getPlugin(), player,
|
||||
() -> DisplayNameUtil.updateDisplayName(module, player, data));
|
||||
FoliaUtil.runAsync(module.getPlugin(),
|
||||
FoliaUtil.runAsync(module.getPlugin(),
|
||||
() -> module.getDataManager().savePlayerData(player.getUniqueId()));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ public class GradientPlayerListener implements Listener {
|
|||
@EventHandler
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
FoliaUtil.runAsync(module.getPlugin(),
|
||||
FoliaUtil.runAsync(module.getPlugin(),
|
||||
() -> module.getDataManager().savePlayerData(player.getUniqueId()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,13 +21,14 @@ public final class DisplayNameUtil {
|
|||
.hexColors()
|
||||
.build();
|
||||
|
||||
private DisplayNameUtil() {}
|
||||
private DisplayNameUtil() {
|
||||
}
|
||||
|
||||
public static void updateDisplayName(GradientModule module, Player player, GradientPlayerData data) {
|
||||
GradientConfig cfg = module.getConfig();
|
||||
String prefix = null;
|
||||
String prefixFormat = cfg.getPrefixFormat();
|
||||
|
||||
|
||||
// Определяем префикс: сначала кастомный, потом LuckPerms
|
||||
if (data.isPrefixEnabled() && data.hasPrefix()) {
|
||||
// Есть кастомный префикс
|
||||
|
|
@ -37,9 +38,9 @@ public final class DisplayNameUtil {
|
|||
String lpPrefix = module.getLuckPermsHook().getActivePrefix(player);
|
||||
if (lpPrefix != null && !lpPrefix.isEmpty()) {
|
||||
String displayName = buildWithLuckPermsPrefix(
|
||||
lpPrefix,
|
||||
player.getName(),
|
||||
data.isColorEnabled() ? data.getColors() : null,
|
||||
lpPrefix,
|
||||
player.getName(),
|
||||
data.isColorEnabled() ? data.getColors() : null,
|
||||
cfg.isUseLegacyRgbFormat(),
|
||||
cfg.isContinuousGradient()
|
||||
);
|
||||
|
|
@ -51,7 +52,7 @@ public final class DisplayNameUtil {
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Строим display name с кастомным префиксом или без префикса
|
||||
String displayName = GradientUtil.buildDisplayName(
|
||||
prefix,
|
||||
|
|
@ -62,7 +63,7 @@ public final class DisplayNameUtil {
|
|||
prefixFormat,
|
||||
cfg.isUseLegacyRgbFormat()
|
||||
);
|
||||
|
||||
|
||||
// Для display name нужен §x формат, конвертируем MiniMessage в legacy
|
||||
displayName = convertMiniMessageToLegacy(displayName);
|
||||
var component = SERIALIZER.deserialize(displayName);
|
||||
|
|
@ -75,12 +76,12 @@ public final class DisplayNameUtil {
|
|||
*/
|
||||
private static String convertMiniMessageToLegacy(String text) {
|
||||
if (text == null) return "";
|
||||
|
||||
|
||||
// Конвертируем <#RRGGBB> в §x§R§R§G§G§B§B
|
||||
Pattern pattern = Pattern.compile("<#([0-9a-fA-F]{6})>");
|
||||
Matcher matcher = pattern.matcher(text);
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
|
||||
while (matcher.find()) {
|
||||
String hex = matcher.group(1).toLowerCase();
|
||||
StringBuilder replacement = new StringBuilder("§x");
|
||||
|
|
@ -90,18 +91,18 @@ public final class DisplayNameUtil {
|
|||
matcher.appendReplacement(sb, replacement.toString());
|
||||
}
|
||||
matcher.appendTail(sb);
|
||||
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String buildWithLuckPermsPrefix(String lpPrefix, String nick,
|
||||
List<String> colors,
|
||||
boolean useLegacyFormat,
|
||||
boolean continuousGradient) {
|
||||
private static String buildWithLuckPermsPrefix(String lpPrefix, String nick,
|
||||
List<String> colors,
|
||||
boolean useLegacyFormat,
|
||||
boolean continuousGradient) {
|
||||
if (colors == null || colors.isEmpty()) {
|
||||
return lpPrefix + nick;
|
||||
}
|
||||
|
||||
|
||||
if (continuousGradient) {
|
||||
String cleanPrefix = stripColors(lpPrefix);
|
||||
String fullText = cleanPrefix + nick;
|
||||
|
|
@ -118,15 +119,15 @@ public final class DisplayNameUtil {
|
|||
|
||||
public static String buildColoredPrefix(GradientModule module, GradientPlayerData data) {
|
||||
if (!data.hasPrefix() || !data.isPrefixEnabled()) return null;
|
||||
|
||||
|
||||
GradientConfig cfg = module.getConfig();
|
||||
String prefix = cfg.getPrefixFormat().replace("{prefix}", data.getPrefix()).stripTrailing();
|
||||
|
||||
|
||||
// Применяем градиент только если цвета включены И есть цвета
|
||||
if (data.hasColors() && data.isColorEnabled() && cfg.isGradientOnPrefix()) {
|
||||
return GradientUtil.applyGradient(prefix, data.getColors(), cfg.isUseLegacyRgbFormat());
|
||||
}
|
||||
|
||||
|
||||
// Возвращаем префикс без цветов
|
||||
return prefix;
|
||||
}
|
||||
|
|
@ -137,12 +138,12 @@ public final class DisplayNameUtil {
|
|||
public static String getFullDisplayName(GradientModule module, Player player) {
|
||||
GradientPlayerData data = module.getDataManager().getPlayerData(player.getUniqueId());
|
||||
GradientConfig cfg = module.getConfig();
|
||||
|
||||
|
||||
String prefix = null;
|
||||
if (data.isPrefixEnabled() && data.hasPrefix()) {
|
||||
prefix = data.getPrefix();
|
||||
}
|
||||
|
||||
|
||||
return GradientUtil.buildDisplayName(
|
||||
prefix,
|
||||
player.getName(),
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ public final class FoliaUtil {
|
|||
IS_FOLIA = checkFolia();
|
||||
}
|
||||
|
||||
private FoliaUtil() {}
|
||||
private FoliaUtil() {
|
||||
}
|
||||
|
||||
private static boolean checkFolia() {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -5,15 +5,14 @@ package com.loki.lochat.gradient.util;
|
|||
*/
|
||||
public final class GradientConstants {
|
||||
|
||||
private GradientConstants() {}
|
||||
|
||||
public static final long MILLIS_PER_SECOND = 1000L;
|
||||
|
||||
// GUI слоты
|
||||
public static final int GUI_CONFIRM_SLOT = 11;
|
||||
public static final int GUI_CANCEL_SLOT = 15;
|
||||
public static final int GUI_PREVIEW_SLOT = 13;
|
||||
|
||||
// LuckPerms
|
||||
public static final int LUCKPERMS_PREFIX_PRIORITY = 100;
|
||||
|
||||
private GradientConstants() {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ public final class GradientUtil {
|
|||
|
||||
private static final Pattern HEX_PATTERN = Pattern.compile("^#[0-9A-Fa-f]{6}$");
|
||||
|
||||
private GradientUtil() {}
|
||||
private GradientUtil() {
|
||||
}
|
||||
|
||||
public static boolean isValidHex(String color) {
|
||||
return HEX_PATTERN.matcher(color).matches();
|
||||
|
|
@ -50,7 +51,7 @@ public final class GradientUtil {
|
|||
// Вычисляем позицию символа от 0.0 до 1.0
|
||||
double ratio = length > 1 ? (double) i / (length - 1) : 0.0;
|
||||
Color interpolated = interpolateMultiColor(colors, ratio);
|
||||
String hex = String.format("#%02x%02x%02x",
|
||||
String hex = String.format("#%02x%02x%02x",
|
||||
interpolated.getRed(), interpolated.getGreen(), interpolated.getBlue());
|
||||
result.append(formatHex(hex, useLegacyFormat)).append(chars[i]);
|
||||
}
|
||||
|
|
@ -142,7 +143,7 @@ public final class GradientUtil {
|
|||
for (int i = 0; i < length; i++) {
|
||||
double ratio = length > 1 ? (double) i / (length - 1) : 0.0;
|
||||
Color interpolated = interpolateMultiColor(colors, ratio);
|
||||
String hex = String.format("#%02x%02x%02x",
|
||||
String hex = String.format("#%02x%02x%02x",
|
||||
interpolated.getRed(), interpolated.getGreen(), interpolated.getBlue());
|
||||
result.append(formatHexForTab(hex)).append(chars[i]);
|
||||
}
|
||||
|
|
@ -151,8 +152,8 @@ public final class GradientUtil {
|
|||
return result.toString();
|
||||
}
|
||||
|
||||
public static String buildDisplayName(String prefix, String nick, List<String> colors,
|
||||
boolean gradientOnPrefix, boolean continuousGradient,
|
||||
public static String buildDisplayName(String prefix, String nick, List<String> colors,
|
||||
boolean gradientOnPrefix, boolean continuousGradient,
|
||||
String prefixFormat, boolean useLegacyFormat) {
|
||||
if (colors == null || colors.isEmpty()) {
|
||||
if (prefix != null && !prefix.isEmpty()) {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ public class LibertyBansHook {
|
|||
public LibertyBansHook(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.muteService = plugin.getServiceRegistry().get(MuteService.class);
|
||||
|
||||
|
||||
// Проверяем наличие LibertyBans
|
||||
if (plugin.getServer().getPluginManager().getPlugin("LibertyBans") != null) {
|
||||
plugin.getLogger().info("LibertyBans обнаружен. Используйте встроенную систему мутов LoChat (/lmute, /lunmute)");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
package com.loki.lochat.integrations;
|
||||
|
||||
import com.loki.lochat.LoChat;
|
||||
import com.loki.lochat.api.service.*;
|
||||
import com.loki.lochat.api.service.ChatService;
|
||||
import com.loki.lochat.api.service.IgnoreService;
|
||||
import com.loki.lochat.api.service.PMService;
|
||||
import com.loki.lochat.api.service.SpyService;
|
||||
import com.loki.lochat.gradient.GradientModule;
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import org.bukkit.Bukkit;
|
||||
|
|
@ -66,25 +69,25 @@ public class PlaceholderAPIHook extends PlaceholderExpansion {
|
|||
yield lastPlayer.getName() != null ? lastPlayer.getName() : "";
|
||||
}
|
||||
case "spy_enabled" -> String.valueOf(spyService.isSpying(uuid));
|
||||
|
||||
|
||||
// Gradient placeholders (совместимость с LoPreff)
|
||||
// Используем TAB формат §x§R§R§G§G§B§B для совместимости с TAB плагином
|
||||
case "gradient_full", "full" -> {
|
||||
Player onlinePlayer = player.getPlayer();
|
||||
if (onlinePlayer == null) yield player.getName() != null ? player.getName() : "";
|
||||
|
||||
|
||||
// Если gradient модуль выключен — берём только LuckPerms префикс + ник
|
||||
if (gradient == null || !gradient.isEnabled()) {
|
||||
yield player.getName() != null ? player.getName() : "";
|
||||
}
|
||||
|
||||
|
||||
// Используем TAB формат для плейсхолдеров
|
||||
yield gradient.getFormattedNameForTab(onlinePlayer);
|
||||
}
|
||||
case "gradient_name", "name" -> {
|
||||
Player onlinePlayer = player.getPlayer();
|
||||
if (onlinePlayer == null) yield player.getName() != null ? player.getName() : "";
|
||||
|
||||
|
||||
if (gradient == null || !gradient.isEnabled()) {
|
||||
yield player.getName();
|
||||
}
|
||||
|
|
@ -93,14 +96,14 @@ public class PlaceholderAPIHook extends PlaceholderExpansion {
|
|||
case "gradient_prefix", "prefix" -> {
|
||||
Player onlinePlayer = player.getPlayer();
|
||||
if (onlinePlayer == null) yield "";
|
||||
|
||||
|
||||
if (gradient == null || !gradient.isEnabled()) yield "";
|
||||
yield gradient.getPrefix(onlinePlayer);
|
||||
}
|
||||
case "lp_prefix" -> {
|
||||
Player onlinePlayer = player.getPlayer();
|
||||
if (onlinePlayer == null) yield "";
|
||||
|
||||
|
||||
if (gradient == null || !gradient.isEnabled()) yield "";
|
||||
yield gradient.getLuckPermsPrefix(onlinePlayer);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@ public class SkinsRestorerHook {
|
|||
Class<?> apiClass = Class.forName("net.skinsrestorer.api.SkinsRestorerAPI");
|
||||
Method getApiMethod = apiClass.getMethod("getApi");
|
||||
skinsAPI = getApiMethod.invoke(null);
|
||||
|
||||
|
||||
getSkinDataMethod = skinsAPI.getClass().getMethod("getSkinData", String.class);
|
||||
|
||||
|
||||
enabled = true;
|
||||
plugin.getLogger().info("SkinsRestorer подключен!");
|
||||
} catch (Exception e) {
|
||||
|
|
@ -52,6 +52,7 @@ public class SkinsRestorerHook {
|
|||
|
||||
/**
|
||||
* Получает текстуру скина игрока
|
||||
*
|
||||
* @param player игрок
|
||||
* @return URL текстуры или null
|
||||
*/
|
||||
|
|
@ -72,6 +73,7 @@ public class SkinsRestorerHook {
|
|||
|
||||
/**
|
||||
* Получает URL головы игрока для отображения
|
||||
*
|
||||
* @param playerName имя игрока
|
||||
* @return URL головы
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ 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.event.player.AsyncChatEvent;
|
||||
import io.papermc.paper.chat.ChatRenderer;
|
||||
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;
|
||||
|
|
@ -25,129 +25,129 @@ public class ChatEventListener implements Listener {
|
|||
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());
|
||||
|
||||
|
||||
boolean allowed = messageService.processMessage(sender, message);
|
||||
|
||||
|
||||
if (!allowed) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
AdvancedMessageFilter.FilterResult filterResult = advancedFilter.filterMessage(sender, message);
|
||||
|
||||
if (!filterResult.isAllowed()) {
|
||||
|
||||
if (!filterResult.allowed()) {
|
||||
event.setCancelled(true);
|
||||
sender.sendMessage(com.loki.lochat.utils.ChatFormatter.parse(filterResult.getBlockReason()));
|
||||
sender.sendMessage(com.loki.lochat.utils.ChatFormatter.parse(filterResult.blockReason()));
|
||||
return;
|
||||
}
|
||||
|
||||
String filteredMessage = filterResult.getFilteredMessage();
|
||||
|
||||
String filteredMessage = filterResult.filteredMessage();
|
||||
event.message(Component.text(filteredMessage));
|
||||
|
||||
|
||||
event.renderer(new EnhancedChatRenderer(sender, event.message()));
|
||||
}
|
||||
|
||||
|
||||
private class EnhancedChatRenderer implements ChatRenderer {
|
||||
private final Player sender;
|
||||
private final Component originalMessage;
|
||||
|
||||
|
||||
public EnhancedChatRenderer(Player sender, Component originalMessage) {
|
||||
this.sender = sender;
|
||||
this.originalMessage = originalMessage;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Component render(Player source, Component sourceDisplayName, Component message, Audience viewer) {
|
||||
String prefix = plugin.getConfig().getString("chat.global.prefix", "[G]");
|
||||
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 Component.text(prefix + " ")
|
||||
.append(playerComponent)
|
||||
.append(Component.text(": "))
|
||||
.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>▶ Клик: написать ЛС"
|
||||
"<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()));
|
||||
|
||||
.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);
|
||||
String urlPattern = "(https?://[\\w\\-._~:/?#\\[\\]@!35538117-c5a8-4bfe-b9f2-b0dcf9374222'()*+,;=%]+)";
|
||||
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)
|
||||
|
||||
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("❤"));
|
||||
|
|
|
|||
|
|
@ -15,12 +15,12 @@ import org.bukkit.event.player.PlayerQuitEvent;
|
|||
public class PlayerEventListener implements Listener {
|
||||
private final PlayerDataService playerDataService;
|
||||
private final NickService nickService;
|
||||
|
||||
|
||||
public PlayerEventListener(ServiceRegistry registry) {
|
||||
this.playerDataService = registry.get(PlayerDataService.class);
|
||||
this.nickService = registry.get(NickService.class);
|
||||
}
|
||||
|
||||
|
||||
@EventHandler
|
||||
public void onJoin(PlayerJoinEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
|
|
@ -29,7 +29,7 @@ public class PlayerEventListener implements Listener {
|
|||
nickService.updatePlayerDisplay(player);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@EventHandler
|
||||
public void onQuit(PlayerQuitEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ public class AutoMessageManager {
|
|||
private final Random random = new Random();
|
||||
private final AtomicInteger currentIndex = new AtomicInteger(0);
|
||||
private boolean running = false;
|
||||
|
||||
|
||||
private Map<String, List<String>> messages;
|
||||
private List<String> messageOrder;
|
||||
private String prefix;
|
||||
|
|
@ -35,21 +35,21 @@ public class AutoMessageManager {
|
|||
if (!file.exists()) {
|
||||
plugin.saveResource("automessages.yml", false);
|
||||
}
|
||||
|
||||
|
||||
FileConfiguration config = YamlConfiguration.loadConfiguration(file);
|
||||
|
||||
|
||||
// Настройки
|
||||
prefix = config.getString("settings.prefix", "&6[&eINFO&6]&r");
|
||||
mode = config.getString("settings.mode", "sequential").toLowerCase();
|
||||
|
||||
|
||||
// Загружаем сообщения
|
||||
messages = new LinkedHashMap<>();
|
||||
ConfigurationSection msgSection = config.getConfigurationSection("messages");
|
||||
|
||||
|
||||
if (msgSection != null) {
|
||||
for (String key : msgSection.getKeys(false)) {
|
||||
List<String> lines = new ArrayList<>();
|
||||
|
||||
|
||||
if (msgSection.isList(key)) {
|
||||
lines.addAll(msgSection.getStringList(key));
|
||||
} else {
|
||||
|
|
@ -58,16 +58,16 @@ public class AutoMessageManager {
|
|||
lines.add(single);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!lines.isEmpty()) {
|
||||
messages.put(key, lines);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Определяем порядок
|
||||
messageOrder = new ArrayList<>();
|
||||
|
||||
|
||||
if (mode.equals("custom")) {
|
||||
String orderStr = config.getString("settings.order", "");
|
||||
for (String item : orderStr.split(",")) {
|
||||
|
|
@ -87,11 +87,11 @@ public class AutoMessageManager {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (messageOrder.isEmpty()) {
|
||||
messageOrder.addAll(messages.keySet());
|
||||
}
|
||||
|
||||
|
||||
plugin.getLogger().info("Загружено " + messages.size() + " автосообщений");
|
||||
}
|
||||
|
||||
|
|
@ -125,14 +125,14 @@ public class AutoMessageManager {
|
|||
}
|
||||
|
||||
String messageKey;
|
||||
|
||||
|
||||
if (mode.equals("random")) {
|
||||
messageKey = messageOrder.get(random.nextInt(messageOrder.size()));
|
||||
} else {
|
||||
int index = currentIndex.getAndUpdate(i -> (i + 1) % messageOrder.size());
|
||||
messageKey = messageOrder.get(index);
|
||||
}
|
||||
|
||||
|
||||
List<String> lines = messages.get(messageKey);
|
||||
if (lines == null || lines.isEmpty()) return;
|
||||
|
||||
|
|
@ -140,7 +140,7 @@ public class AutoMessageManager {
|
|||
for (String line : lines) {
|
||||
// Заменяем плейсхолдеры
|
||||
String processed = line.replace("%online%", String.valueOf(Bukkit.getOnlinePlayers().size()));
|
||||
|
||||
|
||||
// Добавляем префикс к первой непустой строке
|
||||
Component formatted;
|
||||
if (isFirstLine && !line.trim().isEmpty()) {
|
||||
|
|
@ -149,7 +149,7 @@ public class AutoMessageManager {
|
|||
} else {
|
||||
formatted = ChatFormatter.parse(processed);
|
||||
}
|
||||
|
||||
|
||||
for (var player : Bukkit.getOnlinePlayers()) {
|
||||
player.sendMessage(formatted);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,14 +24,15 @@ public class CustomCommandManager {
|
|||
private final LoChat plugin;
|
||||
private final Map<String, CustomCommandData> commands;
|
||||
|
||||
public CustomCommandManager(LoChat plugin) {
|
||||
private CustomCommandManager(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
this.commands = new HashMap<>();
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
loadCommands();
|
||||
|
||||
public static CustomCommandManager create(LoChat plugin) {
|
||||
CustomCommandManager manager = new CustomCommandManager(plugin);
|
||||
manager.loadCommands();
|
||||
return manager;
|
||||
}
|
||||
|
||||
private void loadCommands() {
|
||||
|
|
@ -41,15 +42,15 @@ public class CustomCommandManager {
|
|||
}
|
||||
|
||||
FileConfiguration commandsConfig = YamlConfiguration.loadConfiguration(commandsFile);
|
||||
|
||||
|
||||
// Очищаем старые команды
|
||||
commands.clear();
|
||||
|
||||
|
||||
// Загружаем команды из конфига
|
||||
for (String commandName : commandsConfig.getKeys(false)) {
|
||||
ConfigurationSection section = commandsConfig.getConfigurationSection(commandName);
|
||||
if (section == null) continue;
|
||||
|
||||
|
||||
CustomCommandData data = new CustomCommandData(
|
||||
commandName,
|
||||
section.getString("permission"),
|
||||
|
|
@ -58,13 +59,13 @@ public class CustomCommandManager {
|
|||
section.getString("type", "chat"),
|
||||
section.getString("target", "sender")
|
||||
);
|
||||
|
||||
|
||||
commands.put(commandName, data);
|
||||
|
||||
|
||||
// Регистрируем команду
|
||||
registerCommand(data);
|
||||
}
|
||||
|
||||
|
||||
plugin.getLogger().info("Загружено " + commands.size() + " кастомных команд");
|
||||
}
|
||||
|
||||
|
|
@ -74,17 +75,17 @@ public class CustomCommandManager {
|
|||
Field commandMapField = Bukkit.getServer().getClass().getDeclaredField("commandMap");
|
||||
commandMapField.setAccessible(true);
|
||||
CommandMap commandMap = (CommandMap) commandMapField.get(Bukkit.getServer());
|
||||
|
||||
|
||||
// Создаем и регистрируем команду
|
||||
CustomCommand command = new CustomCommand(data.name, this);
|
||||
commandMap.register("lochat", command);
|
||||
|
||||
|
||||
// Регистрируем алиасы
|
||||
for (String alias : data.aliases) {
|
||||
CustomCommand aliasCommand = new CustomCommand(alias, this);
|
||||
commandMap.register("lochat", aliasCommand);
|
||||
}
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().severe("Ошибка регистрации команды " + data.name + ": " + e.getMessage());
|
||||
}
|
||||
|
|
@ -105,18 +106,18 @@ public class CustomCommandManager {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (data == null) return false;
|
||||
|
||||
|
||||
// Проверяем права
|
||||
if (data.permission != null && !player.hasPermission(data.permission)) {
|
||||
player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getNoPermission()));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Обрабатываем плейсхолдеры
|
||||
String message = processPlaceholders(data.message, player, args);
|
||||
|
||||
|
||||
// Выполняем команду в зависимости от типа
|
||||
switch (data.type.toLowerCase()) {
|
||||
case "broadcast" -> sendBroadcast(data.target, message);
|
||||
|
|
@ -124,26 +125,26 @@ public class CustomCommandManager {
|
|||
case "actionbar" -> sendActionBar(data.target, player, message);
|
||||
default -> sendChatMessage(data.target, player, message);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private String processPlaceholders(String message, Player player, String[] args) {
|
||||
String result = message;
|
||||
|
||||
|
||||
// Базовые плейсхолдеры
|
||||
result = result.replace("{player}", player.getName());
|
||||
result = result.replace("{x}", String.valueOf(player.getLocation().getBlockX()));
|
||||
result = result.replace("{y}", String.valueOf(player.getLocation().getBlockY()));
|
||||
result = result.replace("{z}", String.valueOf(player.getLocation().getBlockZ()));
|
||||
result = result.replace("{world}", player.getWorld().getName());
|
||||
|
||||
|
||||
// Аргументы команды
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
result = result.replace("{arg" + i + "}", args[i]);
|
||||
}
|
||||
result = result.replace("{args}", String.join(" ", args));
|
||||
|
||||
|
||||
// PlaceholderAPI если доступен
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) {
|
||||
try {
|
||||
|
|
@ -156,13 +157,13 @@ public class CustomCommandManager {
|
|||
plugin.getLogger().warning("Ошибка при обработке PlaceholderAPI плейсхолдеров: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void sendChatMessage(String target, Player sender, String message) {
|
||||
Component component = ChatFormatter.parse(message);
|
||||
|
||||
|
||||
switch (target.toLowerCase()) {
|
||||
case "sender" -> sender.sendMessage(component);
|
||||
case "all" -> {
|
||||
|
|
@ -208,10 +209,10 @@ public class CustomCommandManager {
|
|||
String[] parts = message.split("\\n", 2);
|
||||
Component title = ChatFormatter.parse(parts[0]);
|
||||
Component subtitle = parts.length > 1 ? ChatFormatter.parse(parts[1]) : Component.empty();
|
||||
|
||||
Title titleObj = Title.title(title, subtitle,
|
||||
|
||||
Title titleObj = Title.title(title, subtitle,
|
||||
Title.Times.times(Duration.ofMillis(500), Duration.ofSeconds(3), Duration.ofMillis(500)));
|
||||
|
||||
|
||||
switch (target.toLowerCase()) {
|
||||
case "sender" -> sender.showTitle(titleObj);
|
||||
case "all" -> {
|
||||
|
|
@ -236,7 +237,7 @@ public class CustomCommandManager {
|
|||
|
||||
private void sendActionBar(String target, Player sender, String message) {
|
||||
Component component = ChatFormatter.parse(message);
|
||||
|
||||
|
||||
switch (target.toLowerCase()) {
|
||||
case "sender" -> sender.sendActionBar(component);
|
||||
case "all" -> {
|
||||
|
|
@ -263,22 +264,7 @@ public class CustomCommandManager {
|
|||
return commands;
|
||||
}
|
||||
|
||||
public static class CustomCommandData {
|
||||
public final String name;
|
||||
public final String permission;
|
||||
public final String message;
|
||||
public final List<String> aliases;
|
||||
public final String type;
|
||||
public final String target;
|
||||
|
||||
public CustomCommandData(String name, String permission, String message,
|
||||
List<String> aliases, String type, String target) {
|
||||
this.name = name;
|
||||
this.permission = permission;
|
||||
this.message = message;
|
||||
this.aliases = aliases;
|
||||
this.type = type;
|
||||
this.target = target;
|
||||
}
|
||||
public record CustomCommandData(String name, String permission, String message, List<String> aliases, String type,
|
||||
String target) {
|
||||
}
|
||||
}
|
||||
|
|
@ -17,9 +17,14 @@ public class EmojiManager {
|
|||
private boolean enabled;
|
||||
private boolean requirePermission;
|
||||
|
||||
public EmojiManager(LoChat plugin) {
|
||||
private EmojiManager(LoChat plugin) {
|
||||
this.plugin = plugin;
|
||||
load();
|
||||
}
|
||||
|
||||
public static EmojiManager create(LoChat plugin) {
|
||||
EmojiManager manager = new EmojiManager(plugin);
|
||||
manager.load();
|
||||
return manager;
|
||||
}
|
||||
|
||||
public void load() {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import org.bukkit.command.CommandSender;
|
|||
public class ChatFormatter {
|
||||
private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage();
|
||||
private static final LegacyComponentSerializer LEGACY = LegacyComponentSerializer.legacyAmpersand();
|
||||
|
||||
|
||||
public static Object parse(String message) {
|
||||
try {
|
||||
return MINI_MESSAGE.deserialize(message);
|
||||
|
|
@ -19,14 +19,14 @@ public class ChatFormatter {
|
|||
return LEGACY.deserialize(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String toPlain(Object message) {
|
||||
if (message instanceof Component component) {
|
||||
return LegacyComponentSerializer.legacySection().serialize(component);
|
||||
}
|
||||
return message.toString();
|
||||
}
|
||||
|
||||
|
||||
public static void sendMessage(CommandSender sender, Object message) {
|
||||
if (message instanceof Component component) {
|
||||
sender.sendMessage(component);
|
||||
|
|
@ -34,17 +34,17 @@ public class ChatFormatter {
|
|||
sender.sendMessage(message.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void broadcast(Object message) {
|
||||
if (message instanceof Component component) {
|
||||
org.bukkit.Bukkit.broadcast(component);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String stripTags(String message) {
|
||||
return message.replaceAll("<[^>]+>", "").replaceAll("§[0-9a-fk-or]", "");
|
||||
}
|
||||
|
||||
|
||||
public static String convertAllColors(String message) {
|
||||
return message.replace("&", "§");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@ import org.bukkit.entity.Player;
|
|||
* Утилита для проверки расстояния между игроками
|
||||
*/
|
||||
public class DistanceUtil {
|
||||
|
||||
|
||||
public static boolean isInRange(Player player1, Player player2, int radius) {
|
||||
if (!player1.getWorld().equals(player2.getWorld())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return player1.getLocation().distance(player2.getLocation()) <= radius;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import org.bukkit.plugin.java.JavaPlugin;
|
|||
*/
|
||||
public class FoliaUtil {
|
||||
private static boolean isFolia;
|
||||
|
||||
|
||||
static {
|
||||
try {
|
||||
Class.forName("io.papermc.paper.threadedregions.RegionizedServer");
|
||||
|
|
@ -18,11 +18,11 @@ public class FoliaUtil {
|
|||
isFolia = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static boolean isFolia() {
|
||||
return isFolia;
|
||||
}
|
||||
|
||||
|
||||
public static void runAsync(JavaPlugin plugin, Runnable task) {
|
||||
if (isFolia) {
|
||||
Bukkit.getAsyncScheduler().runNow(plugin, scheduledTask -> task.run());
|
||||
|
|
@ -30,7 +30,7 @@ public class FoliaUtil {
|
|||
Bukkit.getScheduler().runTaskAsynchronously(plugin, task);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void runEntityTask(JavaPlugin plugin, Entity entity, Runnable task) {
|
||||
if (isFolia) {
|
||||
entity.getScheduler().run(plugin, scheduledTask -> task.run(), null);
|
||||
|
|
@ -38,12 +38,13 @@ public class FoliaUtil {
|
|||
Bukkit.getScheduler().runTask(plugin, task);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Запускает глобальную повторяющуюся задачу
|
||||
* @param plugin плагин
|
||||
* @param task задача
|
||||
* @param delayTicks задержка перед первым запуском (в тиках)
|
||||
*
|
||||
* @param plugin плагин
|
||||
* @param task задача
|
||||
* @param delayTicks задержка перед первым запуском (в тиках)
|
||||
* @param periodTicks период повторения (в тиках)
|
||||
*/
|
||||
public static void runTimerAsync(JavaPlugin plugin, Runnable task, long delayTicks, long periodTicks) {
|
||||
|
|
|
|||
|
|
@ -4,17 +4,18 @@ package com.loki.lochat.util;
|
|||
* Утилита для парсинга времени (DRY - вынесена общая логика)
|
||||
*/
|
||||
public class TimeParser {
|
||||
|
||||
|
||||
/**
|
||||
* Парсит строку времени в миллисекунды
|
||||
*
|
||||
* @param timeStr строка вида "1d", "2h", "30m", "60s"
|
||||
* @return миллисекунды или -1 при ошибке
|
||||
*/
|
||||
public static long parse(String timeStr) {
|
||||
if (timeStr == null || timeStr.isEmpty()) return 0;
|
||||
|
||||
|
||||
timeStr = timeStr.toLowerCase();
|
||||
|
||||
|
||||
try {
|
||||
if (timeStr.endsWith("d")) {
|
||||
return Long.parseLong(timeStr.replace("d", "")) * 24 * 60 * 60 * 1000L;
|
||||
|
|
@ -31,18 +32,18 @@ public class TimeParser {
|
|||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Форматирует миллисекунды в читаемую строку
|
||||
*/
|
||||
public static String format(long millis) {
|
||||
if (millis <= 0) return "0с";
|
||||
|
||||
|
||||
long seconds = millis / 1000;
|
||||
long minutes = seconds / 60;
|
||||
long hours = minutes / 60;
|
||||
long days = hours / 24;
|
||||
|
||||
|
||||
if (days > 0) {
|
||||
return days + "д " + (hours % 24) + "ч";
|
||||
} else if (hours > 0) {
|
||||
|
|
|
|||
|
|
@ -8,28 +8,28 @@ import org.bukkit.plugin.Plugin;
|
|||
* Менеджер для работы с Adventure API на Arclight/Forge серверах
|
||||
*/
|
||||
public class AdventureManager {
|
||||
|
||||
|
||||
private static boolean adventureAvailable = false;
|
||||
private static Object audiences;
|
||||
|
||||
|
||||
public static void init(Plugin plugin) {
|
||||
try {
|
||||
// Проверяем наличие Adventure API
|
||||
Class.forName("net.kyori.adventure.text.Component");
|
||||
Class<?> audiencesClass = Class.forName("net.kyori.adventure.platform.bukkit.BukkitAudiences");
|
||||
|
||||
|
||||
// Создаем BukkitAudiences через рефлексию
|
||||
Object audiencesInstance = audiencesClass.getMethod("create", Plugin.class).invoke(null, plugin);
|
||||
audiences = audiencesInstance;
|
||||
adventureAvailable = true;
|
||||
|
||||
|
||||
plugin.getLogger().info("Adventure API обнаружен - используем нативную поддержку");
|
||||
} catch (Exception e) {
|
||||
adventureAvailable = false;
|
||||
plugin.getLogger().info("Adventure API не найден - используем legacy режим");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void shutdown() {
|
||||
if (audiences != null && adventureAvailable) {
|
||||
try {
|
||||
|
|
@ -41,7 +41,7 @@ public class AdventureManager {
|
|||
}
|
||||
adventureAvailable = false;
|
||||
}
|
||||
|
||||
|
||||
public static void sendMessage(CommandSender sender, Object message) {
|
||||
if (adventureAvailable && audiences != null) {
|
||||
try {
|
||||
|
|
@ -53,7 +53,7 @@ public class AdventureManager {
|
|||
// Fallback на legacy
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Legacy fallback
|
||||
if (message instanceof String) {
|
||||
sender.sendMessage((String) message);
|
||||
|
|
@ -61,7 +61,7 @@ public class AdventureManager {
|
|||
sender.sendMessage(message.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void sendMessage(Player player, Object message) {
|
||||
if (adventureAvailable && audiences != null) {
|
||||
try {
|
||||
|
|
@ -73,7 +73,7 @@ public class AdventureManager {
|
|||
// Fallback на legacy
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Legacy fallback
|
||||
if (message instanceof String) {
|
||||
player.sendMessage((String) message);
|
||||
|
|
@ -81,7 +81,7 @@ public class AdventureManager {
|
|||
player.sendMessage(message.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static boolean isAdventureAvailable() {
|
||||
return adventureAvailable;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ public final class ChatFormatter {
|
|||
private static final PlainTextComponentSerializer PLAIN =
|
||||
PlainTextComponentSerializer.plainText();
|
||||
|
||||
private ChatFormatter() {}
|
||||
private ChatFormatter() {
|
||||
}
|
||||
|
||||
/* ===================== BASIC ===================== */
|
||||
|
||||
|
|
@ -38,7 +39,7 @@ public final class ChatFormatter {
|
|||
*/
|
||||
private static String convertLegacyFormats(String message) {
|
||||
if (message == null) return "";
|
||||
|
||||
|
||||
// Конвертируем &#RRGGBB в <#RRGGBB>
|
||||
java.util.regex.Pattern pattern1 = java.util.regex.Pattern.compile("&#([0-9a-fA-F]{6})");
|
||||
java.util.regex.Matcher matcher1 = pattern1.matcher(message);
|
||||
|
|
@ -49,7 +50,7 @@ public final class ChatFormatter {
|
|||
}
|
||||
matcher1.appendTail(sb1);
|
||||
message = sb1.toString();
|
||||
|
||||
|
||||
// Конвертируем #RRGGBB в <#RRGGBB> (только если не внутри тегов)
|
||||
java.util.regex.Pattern pattern2 = java.util.regex.Pattern.compile("(?<!<)#([0-9a-fA-F]{6})(?![^<]*>)");
|
||||
java.util.regex.Matcher matcher2 = pattern2.matcher(message);
|
||||
|
|
@ -60,7 +61,7 @@ public final class ChatFormatter {
|
|||
}
|
||||
matcher2.appendTail(sb2);
|
||||
message = sb2.toString();
|
||||
|
||||
|
||||
// Конвертируем §[0-9a-fk-or] в соответствующие MiniMessage теги
|
||||
message = message.replaceAll("§0", "<black>");
|
||||
message = message.replaceAll("§1", "<dark_blue>");
|
||||
|
|
@ -84,7 +85,7 @@ public final class ChatFormatter {
|
|||
message = message.replaceAll("§n", "<underlined>");
|
||||
message = message.replaceAll("§o", "<italic>");
|
||||
message = message.replaceAll("§r", "<reset>");
|
||||
|
||||
|
||||
// Конвертируем &[0-9a-fk-or] в соответствующие MiniMessage теги
|
||||
message = message.replaceAll("&0", "<black>");
|
||||
message = message.replaceAll("&1", "<dark_blue>");
|
||||
|
|
@ -102,7 +103,7 @@ public final class ChatFormatter {
|
|||
message = message.replaceAll("&d", "<light_purple>");
|
||||
message = message.replaceAll("&e", "<yellow>");
|
||||
message = message.replaceAll("&f", "<white>");
|
||||
|
||||
|
||||
// Конвертируем форматирование
|
||||
message = message.replaceAll("&k", "<obfuscated>");
|
||||
message = message.replaceAll("&l", "<bold>");
|
||||
|
|
@ -110,7 +111,7 @@ public final class ChatFormatter {
|
|||
message = message.replaceAll("&n", "<underlined>");
|
||||
message = message.replaceAll("&o", "<italic>");
|
||||
message = message.replaceAll("&r", "<reset>");
|
||||
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
|
|
@ -122,7 +123,7 @@ public final class ChatFormatter {
|
|||
public static String toPlain(Component component) {
|
||||
return component == null ? "" : PLAIN.serialize(component);
|
||||
}
|
||||
|
||||
|
||||
public static String toPlain(Object obj) {
|
||||
if (obj == null) return "";
|
||||
if (obj instanceof Component component) {
|
||||
|
|
@ -130,7 +131,7 @@ public final class ChatFormatter {
|
|||
}
|
||||
return obj.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Отправляет сообщение игроку
|
||||
*/
|
||||
|
|
@ -190,7 +191,7 @@ public final class ChatFormatter {
|
|||
// Получаем отображаемые имена игроков
|
||||
String senderDisplay = getPlayerDisplayName(sender);
|
||||
String receiverDisplay = getPlayerDisplayName(receiver);
|
||||
|
||||
|
||||
return parse(format
|
||||
.replace("{sender}", sender.getName())
|
||||
.replace("{receiver}", receiver.getName())
|
||||
|
|
@ -208,7 +209,7 @@ public final class ChatFormatter {
|
|||
// Получаем отображаемые имена игроков
|
||||
String senderDisplay = getPlayerDisplayName(sender);
|
||||
String receiverDisplay = getPlayerDisplayName(receiver);
|
||||
|
||||
|
||||
return parse(format
|
||||
.replace("{sender}", sender.getName())
|
||||
.replace("{receiver}", receiver.getName())
|
||||
|
|
@ -223,12 +224,12 @@ public final class ChatFormatter {
|
|||
private static String getPlayerDisplayName(Player player) {
|
||||
// Получаем экземпляр плагина
|
||||
com.loki.lochat.LoChat plugin = com.loki.lochat.LoChat.getInstance();
|
||||
|
||||
|
||||
// Если градиентный модуль включен, используем его
|
||||
if (plugin.getGradientModule() != null && plugin.getGradientModule().isEnabled()) {
|
||||
return plugin.getGradientModule().getFormattedName(player);
|
||||
}
|
||||
|
||||
|
||||
// Иначе просто имя игрока
|
||||
return player.getName();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,27 +5,24 @@ package com.loki.lochat.utils;
|
|||
*/
|
||||
public final class Constants {
|
||||
|
||||
private Constants() {}
|
||||
|
||||
// Чат
|
||||
public static final int DEFAULT_LOCAL_RADIUS = 100;
|
||||
public static final int CLEAR_CHAT_LINES = 100;
|
||||
|
||||
// Время
|
||||
public static final long MILLIS_IN_SECOND = 1000L;
|
||||
public static final int SECONDS_IN_MINUTE = 60;
|
||||
public static final int MINUTES_IN_HOUR = 60;
|
||||
public static final int HOURS_IN_DAY = 24;
|
||||
|
||||
// Градиенты
|
||||
public static final int DEFAULT_PRICE_PER_COLOR = 50;
|
||||
public static final int DEFAULT_PREFIX_PRICE = 500;
|
||||
public static final int LUCKPERMS_PREFIX_PRIORITY = 100;
|
||||
|
||||
// Folia/Paper совместимость
|
||||
public static final int TICKS_PER_SECOND = 20;
|
||||
public static final int MILLIS_PER_TICK = 50;
|
||||
|
||||
// Анти-спам
|
||||
public static final int PERCENTAGE_MULTIPLIER = 100;
|
||||
|
||||
private Constants() {
|
||||
}
|
||||
}
|
||||
|
|
@ -5,22 +5,23 @@ import org.bukkit.entity.Player;
|
|||
|
||||
public class DistanceUtil {
|
||||
|
||||
private 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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,22 +16,22 @@ import java.util.regex.Matcher;
|
|||
import java.util.regex.Pattern;
|
||||
|
||||
public class MentionHandler {
|
||||
|
||||
|
||||
private static final Pattern EVERYONE_PATTERN = Pattern.compile("@everyone\\b");
|
||||
private static final Pattern HERE_PATTERN = Pattern.compile("@here\\b");
|
||||
private static final Pattern ROLE_PATTERN = Pattern.compile("@role:([\\w.]+)");
|
||||
private static final Pattern PLAYER_PATTERN = Pattern.compile("@(\\w+)");
|
||||
|
||||
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
|
||||
public MentionHandler(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
|
||||
public Component processMentions(Component message, Player sender, Player viewer) {
|
||||
String plainText = PlainTextComponentSerializer.plainText().serialize(message);
|
||||
List<Player> mentionedPlayers = new ArrayList<>();
|
||||
|
||||
|
||||
if (sender.hasPermission("lochat.mention.everyone")) {
|
||||
Matcher everyoneMatcher = EVERYONE_PATTERN.matcher(plainText);
|
||||
if (everyoneMatcher.find()) {
|
||||
|
|
@ -39,7 +39,7 @@ public class MentionHandler {
|
|||
message = highlightMention(message, "@everyone");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (sender.hasPermission("lochat.mention.here")) {
|
||||
Matcher hereMatcher = HERE_PATTERN.matcher(plainText);
|
||||
if (hereMatcher.find()) {
|
||||
|
|
@ -47,57 +47,57 @@ public class MentionHandler {
|
|||
message = highlightMention(message, "@here");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (sender.hasPermission("lochat.mention.role")) {
|
||||
Matcher roleMatcher = ROLE_PATTERN.matcher(plainText);
|
||||
while (roleMatcher.find()) {
|
||||
String permission = roleMatcher.group(1);
|
||||
String fullMention = "@role:" + permission;
|
||||
|
||||
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (player.hasPermission(permission)) {
|
||||
mentionedPlayers.add(player);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
message = highlightMention(message, fullMention);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Matcher playerMatcher = PLAYER_PATTERN.matcher(plainText);
|
||||
while (playerMatcher.find()) {
|
||||
String playerName = playerMatcher.group(1);
|
||||
Player mentioned = Bukkit.getPlayerExact(playerName);
|
||||
|
||||
|
||||
if (mentioned != null && mentioned.isOnline()) {
|
||||
mentionedPlayers.add(mentioned);
|
||||
message = highlightMention(message, "@" + playerName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (mentionedPlayers.contains(viewer)) {
|
||||
playMentionSound(viewer);
|
||||
message = highlightForViewer(message, viewer);
|
||||
}
|
||||
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
private Component highlightMention(Component message, String mention) {
|
||||
return message.replaceText(builder ->
|
||||
builder.matchLiteral(mention)
|
||||
.replacement(Component.text(mention, NamedTextColor.YELLOW, TextDecoration.BOLD))
|
||||
return message.replaceText(builder ->
|
||||
builder.matchLiteral(mention)
|
||||
.replacement(Component.text(mention, NamedTextColor.YELLOW, TextDecoration.BOLD))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
private Component highlightForViewer(Component message, Player viewer) {
|
||||
String viewerMention = "@" + viewer.getName();
|
||||
return message.replaceText(builder ->
|
||||
builder.matchLiteral(viewerMention)
|
||||
.replacement(Component.text(viewerMention, NamedTextColor.GOLD, TextDecoration.BOLD))
|
||||
return message.replaceText(builder ->
|
||||
builder.matchLiteral(viewerMention)
|
||||
.replacement(Component.text(viewerMention, NamedTextColor.GOLD, TextDecoration.BOLD))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
private void playMentionSound(Player player) {
|
||||
FoliaUtil.runEntityTask(plugin, player, () -> {
|
||||
player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0f, 1.5f);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.loki.lochat.utils;
|
|||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -11,10 +12,32 @@ import java.util.UUID;
|
|||
*/
|
||||
public final class PlayerUtil {
|
||||
|
||||
private PlayerUtil() {}
|
||||
private PlayerUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely parses a sound name from config.
|
||||
* Uses deprecated Sound.valueOf() for backward compatibility with string-based config values.
|
||||
*
|
||||
* @param soundName the sound name to parse
|
||||
* @param defaultSound the default sound to return if parsing fails
|
||||
* @return the parsed Sound or the default if invalid
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public static Sound parseSound(String soundName, Sound defaultSound) {
|
||||
if (soundName == null || soundName.isEmpty()) {
|
||||
return defaultSound;
|
||||
}
|
||||
try {
|
||||
return Sound.valueOf(soundName.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
return defaultSound;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Находит игрока по имени (онлайн или оффлайн)
|
||||
*
|
||||
* @param name имя игрока
|
||||
* @return UUID игрока или null если не найден
|
||||
*/
|
||||
|
|
@ -36,6 +59,7 @@ public final class PlayerUtil {
|
|||
|
||||
/**
|
||||
* Проверяет, существует ли игрок (играл на сервере)
|
||||
*
|
||||
* @param name имя игрока
|
||||
* @return true если игрок существует
|
||||
*/
|
||||
|
|
@ -45,6 +69,7 @@ public final class PlayerUtil {
|
|||
|
||||
/**
|
||||
* Получает имя игрока по UUID
|
||||
*
|
||||
* @param uuid UUID игрока
|
||||
* @return имя игрока или null
|
||||
*/
|
||||
|
|
@ -60,6 +85,7 @@ public final class PlayerUtil {
|
|||
|
||||
/**
|
||||
* Проверяет, онлайн ли игрок
|
||||
*
|
||||
* @param uuid UUID игрока
|
||||
* @return true если игрок онлайн
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -7,42 +7,42 @@ import java.util.regex.Matcher;
|
|||
import java.util.regex.Pattern;
|
||||
|
||||
public class TextFormatter {
|
||||
|
||||
|
||||
private static final Pattern BOLD_PATTERN = Pattern.compile("\\*\\*(.+?)\\*\\*");
|
||||
private static final Pattern ITALIC_PATTERN = Pattern.compile("\\*(.+?)\\*");
|
||||
private static final Pattern UNDERLINE_PATTERN = Pattern.compile("__(.+?)__");
|
||||
private static final Pattern STRIKETHROUGH_PATTERN = Pattern.compile("~~(.+?)~~");
|
||||
private static final Pattern CODE_PATTERN = Pattern.compile("`(.+?)`");
|
||||
|
||||
|
||||
public static Component formatMarkdown(Component message) {
|
||||
String text = PlainTextComponentSerializer.plainText().serialize(message);
|
||||
|
||||
|
||||
text = formatBold(text);
|
||||
text = formatItalic(text);
|
||||
text = formatUnderline(text);
|
||||
text = formatStrikethrough(text);
|
||||
text = formatCode(text);
|
||||
|
||||
|
||||
return ChatFormatter.parse(text);
|
||||
}
|
||||
|
||||
|
||||
private static String formatBold(String text) {
|
||||
Matcher matcher = BOLD_PATTERN.matcher(text);
|
||||
StringBuffer result = new StringBuffer();
|
||||
|
||||
|
||||
while (matcher.find()) {
|
||||
String content = matcher.group(1);
|
||||
matcher.appendReplacement(result, "<bold>" + content + "</bold>");
|
||||
}
|
||||
matcher.appendTail(result);
|
||||
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
|
||||
private static String formatItalic(String text) {
|
||||
Matcher matcher = ITALIC_PATTERN.matcher(text);
|
||||
StringBuffer result = new StringBuffer();
|
||||
|
||||
|
||||
while (matcher.find()) {
|
||||
String content = matcher.group(1);
|
||||
if (!content.startsWith("*") && !content.endsWith("*")) {
|
||||
|
|
@ -50,46 +50,46 @@ public class TextFormatter {
|
|||
}
|
||||
}
|
||||
matcher.appendTail(result);
|
||||
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
|
||||
private static String formatUnderline(String text) {
|
||||
Matcher matcher = UNDERLINE_PATTERN.matcher(text);
|
||||
StringBuffer result = new StringBuffer();
|
||||
|
||||
|
||||
while (matcher.find()) {
|
||||
String content = matcher.group(1);
|
||||
matcher.appendReplacement(result, "<underlined>" + content + "</underlined>");
|
||||
}
|
||||
matcher.appendTail(result);
|
||||
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
|
||||
private static String formatStrikethrough(String text) {
|
||||
Matcher matcher = STRIKETHROUGH_PATTERN.matcher(text);
|
||||
StringBuffer result = new StringBuffer();
|
||||
|
||||
|
||||
while (matcher.find()) {
|
||||
String content = matcher.group(1);
|
||||
matcher.appendReplacement(result, "<strikethrough>" + content + "</strikethrough>");
|
||||
}
|
||||
matcher.appendTail(result);
|
||||
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
|
||||
private static String formatCode(String text) {
|
||||
Matcher matcher = CODE_PATTERN.matcher(text);
|
||||
StringBuffer result = new StringBuffer();
|
||||
|
||||
|
||||
while (matcher.find()) {
|
||||
String content = matcher.group(1);
|
||||
matcher.appendReplacement(result, "󙀸`" + content + "`");
|
||||
}
|
||||
matcher.appendTail(result);
|
||||
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue