From f52bd29e7d7a5905af7c7001282e987fb230139f Mon Sep 17 00:00:00 2001 From: loki5512344 Date: Tue, 30 Dec 2025 12:29:39 +0100 Subject: [PATCH] =?UTF-8?q?v1.5.0:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BA=D0=B0=D1=81=D1=82=D0=BE=D0=BC=D0=BD?= =?UTF-8?q?=D1=8B=D0=B5=20=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4=D1=8B,=20?= =?UTF-8?q?=D0=B0=D0=BB=D0=B8=D0=B0=D1=81=D1=8B,=20=D1=86=D0=B2=D0=B5?= =?UTF-8?q?=D1=82=D0=B0=20=D1=87=D0=B0=D1=82=D0=B0,=20=D0=BC=D0=BD=D0=BE?= =?UTF-8?q?=D0=B3=D0=BE=D1=8F=D0=B4=D0=B5=D1=80=D0=BD=D0=B0=D1=8F=20=D1=81?= =?UTF-8?q?=D0=B1=D0=BE=D1=80=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Система кастомных команд с алиасами (custom-commands.yml) - Команда /chatcolor для установки цвета сообщений - Исправлена ошибка с legacy кодами в PM - Настройки объявлений (title, actionbar) - Автообновление конфигов - Отображение префикса над игроком - Многоядерная сборка Gradle - Добавлены алиасы для команд (русские и английские) - Права на использование цветовых кодов --- build.gradle | 10 +- gradle.properties | 13 + src/main/java/com/loki/lochat/LoChat.java | 19 ++ .../loki/lochat/commands/AnnounceCommand.java | 26 +- .../lochat/commands/ChatColorCommand.java | 86 ++++++ .../loki/lochat/commands/CustomCommand.java | 38 +++ .../loki/lochat/commands/LoChatCommand.java | 74 ++++- .../com/loki/lochat/config/ConfigManager.java | 87 ++++++ .../gradient/config/GradientConfig.java | 4 + .../listeners/GradientPlayerListener.java | 5 +- .../lochat/managers/ChatColorManager.java | 93 ++++++ .../lochat/managers/CustomCommandManager.java | 278 ++++++++++++++++++ .../com/loki/lochat/utils/ChatFormatter.java | 16 +- src/main/resources/config.yml | 14 + src/main/resources/custom-commands.yml | 51 ++++ src/main/resources/gradient-config.yml | 7 + src/main/resources/plugin.yml | 20 +- 17 files changed, 808 insertions(+), 33 deletions(-) create mode 100644 gradle.properties create mode 100644 src/main/java/com/loki/lochat/commands/ChatColorCommand.java create mode 100644 src/main/java/com/loki/lochat/commands/CustomCommand.java create mode 100644 src/main/java/com/loki/lochat/managers/ChatColorManager.java create mode 100644 src/main/java/com/loki/lochat/managers/CustomCommandManager.java create mode 100644 src/main/resources/custom-commands.yml diff --git a/build.gradle b/build.gradle index 580b170..152d866 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { } group = 'com.loki' -version = '1.0.0' +version = '1.5.0' repositories { mavenCentral() @@ -29,14 +29,20 @@ java { tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' + // Параллельная компиляция + options.fork = true + options.forkOptions.jvmArgs += ['-XX:+UseParallelGC'] } test { useJUnitPlatform() + // Параллельное выполнение тестов + maxParallelForks = Runtime.runtime.availableProcessors() + forkEvery = 100 } jar { - archiveFileName = 'LoChat-1.0.0.jar' + archiveFileName = 'LoChat-1.5.0.jar' from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..ffbcc4b --- /dev/null +++ b/gradle.properties @@ -0,0 +1,13 @@ +# Параллельная сборка +org.gradle.parallel=true +org.gradle.workers.max=8 +org.gradle.configureondemand=true + +# Кэширование +org.gradle.caching=true + +# JVM настройки для Gradle +org.gradle.jvmargs=-Xmx2g -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError + +# Kotlin DSL +kotlin.code.style=official \ No newline at end of file diff --git a/src/main/java/com/loki/lochat/LoChat.java b/src/main/java/com/loki/lochat/LoChat.java index 20fbbba..d1acd81 100644 --- a/src/main/java/com/loki/lochat/LoChat.java +++ b/src/main/java/com/loki/lochat/LoChat.java @@ -26,6 +26,8 @@ public final class LoChat extends JavaPlugin { private MentionManager mentionManager; private SpyManager spyManager; private EmojiManager emojiManager; + private ChatColorManager chatColorManager; + private CustomCommandManager customCommandManager; private GradientModule gradientModule; @Override @@ -45,6 +47,8 @@ public final class LoChat extends JavaPlugin { cooldownManager = new CooldownManager(); mentionManager = new MentionManager(this); spyManager = new SpyManager(this); + chatColorManager = new ChatColorManager(this); + customCommandManager = new CustomCommandManager(this); chatManager = new ChatManager(this); autoMessageManager = new AutoMessageManager(this); @@ -80,6 +84,9 @@ public final class LoChat extends JavaPlugin { if (ignoreManager != null) { ignoreManager.save(); } + if (chatColorManager != null) { + chatColorManager.saveData(); + } if (gradientModule != null) { gradientModule.shutdown(); } @@ -97,6 +104,7 @@ public final class LoChat extends JavaPlugin { getCommand("announce").setExecutor(new AnnounceCommand(this)); getCommand("chatspy").setExecutor(new ChatSpyCommand(this)); getCommand("clearchat").setExecutor(new ClearChatCommand(this)); + getCommand("chatcolor").setExecutor(new ChatColorCommand(this)); LoChatCommand loChatCommand = new LoChatCommand(this); getCommand("lochat").setExecutor(loChatCommand); @@ -112,6 +120,9 @@ public final class LoChat extends JavaPlugin { if (gradientModule != null) { gradientModule.reload(); } + if (customCommandManager != null) { + customCommandManager.reload(); + } } public static LoChat getInstance() { @@ -169,4 +180,12 @@ public final class LoChat extends JavaPlugin { public GradientModule getGradientModule() { return gradientModule; } + + public ChatColorManager getChatColorManager() { + return chatColorManager; + } + + public CustomCommandManager getCustomCommandManager() { + return customCommandManager; + } } diff --git a/src/main/java/com/loki/lochat/commands/AnnounceCommand.java b/src/main/java/com/loki/lochat/commands/AnnounceCommand.java index 2c46457..a993d21 100644 --- a/src/main/java/com/loki/lochat/commands/AnnounceCommand.java +++ b/src/main/java/com/loki/lochat/commands/AnnounceCommand.java @@ -43,16 +43,24 @@ public class AnnounceCommand implements CommandExecutor { for (Player player : Bukkit.getOnlinePlayers()) { player.sendMessage(formatted); - // ActionBar - player.sendActionBar(formatted); + // ActionBar (если включен) + if (plugin.getConfigManager().isAnnouncementActionBarEnabled()) { + player.sendActionBar(formatted); + } - // Title - Title title = Title.title( - ChatFormatter.parse("ОБЪЯВЛЕНИЕ"), - ChatFormatter.parse(message), - Title.Times.times(Duration.ofMillis(500), Duration.ofSeconds(3), Duration.ofMillis(500)) - ); - player.showTitle(title); + // 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) + ) + ); + player.showTitle(title); + } } plugin.getLogger().info("[ANNOUNCE] " + sender.getName() + ": " + ChatFormatter.stripTags(message)); diff --git a/src/main/java/com/loki/lochat/commands/ChatColorCommand.java b/src/main/java/com/loki/lochat/commands/ChatColorCommand.java new file mode 100644 index 0000000..4c43009 --- /dev/null +++ b/src/main/java/com/loki/lochat/commands/ChatColorCommand.java @@ -0,0 +1,86 @@ +package com.loki.lochat.commands; + +import com.loki.lochat.LoChat; +import com.loki.lochat.utils.ChatFormatter; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ChatColorCommand implements CommandExecutor, TabCompleter { + + private final LoChat plugin; + + public ChatColorCommand(LoChat plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + if (!(sender instanceof Player player)) { + sender.sendMessage("Только для игроков!"); + return true; + } + + if (!player.hasPermission("lochat.chatcolor")) { + player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getNoPermission())); + return true; + } + + if (args.length == 0) { + player.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getInvalidUsage("/chatcolor <цвет>"))); + player.sendMessage(ChatFormatter.parse("Доступные цвета: red, blue, green, yellow, gold, aqua, light_purple, white, gray, dark_gray")); + return true; + } + + String color = args[0].toLowerCase(); + + // Проверяем валидность цвета + if (!isValidColor(color)) { + player.sendMessage(ChatFormatter.parse("Неверный цвет! Используйте один из доступных цветов.")); + return true; + } + + // Сохраняем цвет чата игрока + plugin.getChatColorManager().setChatColor(player.getUniqueId(), color); + player.sendMessage(ChatFormatter.parse("Цвет чата установлен на <" + color + ">" + color + "!")); + + return true; + } + + private boolean isValidColor(String color) { + List validColors = Arrays.asList( + "red", "blue", "green", "yellow", "gold", "aqua", + "light_purple", "white", "gray", "dark_gray", "black", + "dark_red", "dark_green", "dark_blue", "dark_aqua", "dark_purple" + ); + return validColors.contains(color); + } + + @Override + public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { + if (args.length == 1) { + List colors = Arrays.asList( + "red", "blue", "green", "yellow", "gold", "aqua", + "light_purple", "white", "gray", "dark_gray" + ); + List completions = new ArrayList<>(); + String input = args[0].toLowerCase(); + + for (String color : colors) { + if (color.startsWith(input)) { + completions.add(color); + } + } + return completions; + } + return new ArrayList<>(); + } +} \ No newline at end of file diff --git a/src/main/java/com/loki/lochat/commands/CustomCommand.java b/src/main/java/com/loki/lochat/commands/CustomCommand.java new file mode 100644 index 0000000..6570d75 --- /dev/null +++ b/src/main/java/com/loki/lochat/commands/CustomCommand.java @@ -0,0 +1,38 @@ +package com.loki.lochat.commands; + +import com.loki.lochat.managers.CustomCommandManager; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public class CustomCommand extends Command { + + private final CustomCommandManager manager; + + public CustomCommand(String name, CustomCommandManager manager) { + super(name); + this.manager = manager; + this.setDescription("Кастомная команда LoChat"); + this.setUsage("/" + name); + } + + @Override + public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { + if (!(sender instanceof Player player)) { + sender.sendMessage("Только для игроков!"); + return true; + } + + return manager.executeCustomCommand(commandLabel, player, args); + } + + @Override + public @NotNull List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException { + // Базовое автодополнение - можно расширить при необходимости + return new ArrayList<>(); + } +} \ No newline at end of file diff --git a/src/main/java/com/loki/lochat/commands/LoChatCommand.java b/src/main/java/com/loki/lochat/commands/LoChatCommand.java index 8b3bd09..a4e4105 100644 --- a/src/main/java/com/loki/lochat/commands/LoChatCommand.java +++ b/src/main/java/com/loki/lochat/commands/LoChatCommand.java @@ -1,6 +1,7 @@ package com.loki.lochat.commands; import com.loki.lochat.LoChat; +import com.loki.lochat.utils.ChatFormatter; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; @@ -8,6 +9,7 @@ import org.bukkit.command.TabCompleter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; import java.util.List; public class LoChatCommand implements CommandExecutor, TabCompleter { @@ -21,35 +23,75 @@ public class LoChatCommand implements CommandExecutor, TabCompleter { @Override public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + if (!sender.hasPermission("lochat.admin")) { + sender.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getNoPermission())); + return true; + } + if (args.length == 0) { - sender.sendMessage("§e=== LoChat v" + plugin.getDescription().getVersion() + " ==="); - sender.sendMessage("§e/lochat reload §7— перезагрузить конфиги"); + sender.sendMessage(ChatFormatter.parse("LoChat v" + plugin.getDescription().getVersion() + "")); + sender.sendMessage(ChatFormatter.parse("Команды:")); + sender.sendMessage(ChatFormatter.parse("/lochat reload - Перезагрузить конфиги")); + sender.sendMessage(ChatFormatter.parse("/lochat commands - Список кастомных команд")); + sender.sendMessage(ChatFormatter.parse("/lochat commands reload - Перезагрузить кастомные команды")); return true; } - if (args[0].equalsIgnoreCase("reload")) { - if (!sender.hasPermission("lochat.admin")) { - sender.sendMessage(plugin.getMessageConfig().get("errors.no-permission")); - return true; + switch (args[0].toLowerCase()) { + case "reload" -> { + plugin.reload(); + sender.sendMessage(ChatFormatter.parse("Конфигурация перезагружена!")); } - - plugin.reload(); - sender.sendMessage("§aLoChat перезагружен!"); - return true; + case "commands" -> { + if (args.length > 1 && args[1].equalsIgnoreCase("reload")) { + plugin.getCustomCommandManager().reload(); + sender.sendMessage(ChatFormatter.parse("Кастомные команды перезагружены!")); + } else { + var commands = plugin.getCustomCommandManager().getCommands(); + sender.sendMessage(ChatFormatter.parse("Кастомные команды (" + commands.size() + "):")); + for (var cmd : commands.values()) { + sender.sendMessage(ChatFormatter.parse("/" + cmd.name + " (" + cmd.type + ")")); + if (!cmd.aliases.isEmpty()) { + sender.sendMessage(ChatFormatter.parse(" Алиасы: " + String.join(", ", cmd.aliases) + "")); + } + } + } + } + default -> sender.sendMessage(ChatFormatter.parse("Неизвестная команда. Используйте /lochat для помощи.")); } - sender.sendMessage("§cНеизвестная команда. Используй /lochat reload"); return true; } @Override public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { - if (args.length == 1 && sender.hasPermission("lochat.admin")) { - return List.of("reload").stream() - .filter(s -> s.startsWith(args[0].toLowerCase())) - .toList(); + if (!sender.hasPermission("lochat.admin")) { + return new ArrayList<>(); } - return List.of(); + + if (args.length == 1) { + List completions = new ArrayList<>(); + String input = args[0].toLowerCase(); + + for (String cmd : List.of("reload", "commands")) { + if (cmd.startsWith(input)) { + completions.add(cmd); + } + } + return completions; + } + + if (args.length == 2 && args[0].equalsIgnoreCase("commands")) { + List completions = new ArrayList<>(); + String input = args[1].toLowerCase(); + + if ("reload".startsWith(input)) { + completions.add("reload"); + } + return completions; + } + + return new ArrayList<>(); } } diff --git a/src/main/java/com/loki/lochat/config/ConfigManager.java b/src/main/java/com/loki/lochat/config/ConfigManager.java index 1e26d55..cd6247e 100644 --- a/src/main/java/com/loki/lochat/config/ConfigManager.java +++ b/src/main/java/com/loki/lochat/config/ConfigManager.java @@ -14,6 +14,76 @@ public class ConfigManager { this.plugin = plugin; plugin.saveDefaultConfig(); this.config = plugin.getConfig(); + + // Проверяем версию конфига и обновляем при необходимости + updateConfig(); + } + + 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("Конфиг успешно обновлен!"); + } + } + + private void addMissingConfigOptions() { + // Добавляем настройки объявлений если их нет + if (!config.contains("announcements")) { + config.set("announcements.show-title", true); + config.set("announcements.title-header", "ОБЪЯВЛЕНИЕ"); + 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); + config.set("mentions.sound-type", "BLOCK_NOTE_BLOCK_PLING"); + config.set("mentions.highlight", "@{player}"); + config.set("mentions.self-highlight", "{player}"); + } + + if (!config.contains("filter.enabled")) { + config.set("filter.enabled", true); + config.set("filter.words", java.util.Arrays.asList("хуй", "пизд", "блять", "ебать", "сука")); + config.set("filter.action", "censor"); + config.set("filter.replacement", "***"); + } + + if (!config.contains("antispam.enabled")) { + config.set("antispam.enabled", true); + config.set("antispam.max-caps-percent", 70); + config.set("antispam.max-repeat-chars", 5); + config.set("antispam.similar-message-delay", 30); + } + + if (!config.contains("automessages.enabled")) { + config.set("automessages.enabled", true); + config.set("automessages.interval", 300); + config.set("automessages.random", false); + } + + if (!config.contains("lopreff.enabled")) { + config.set("lopreff.enabled", true); + config.set("lopreff.use-gradient-name", true); + config.set("lopreff.use-custom-prefix", true); + } } public void reload() { @@ -153,6 +223,23 @@ public class ConfigManager { return config.getString("formats.announcement", "[ОБЪЯВЛЕНИЕ] "); } + // Announcements + public boolean isAnnouncementTitleEnabled() { + return config.getBoolean("announcements.show-title", true); + } + + public String getAnnouncementTitleHeader() { + return config.getString("announcements.title-header", "ОБЪЯВЛЕНИЕ"); + } + + public boolean isAnnouncementActionBarEnabled() { + return config.getBoolean("announcements.show-actionbar", true); + } + + public int getAnnouncementTitleDuration() { + return config.getInt("announcements.title-duration", 3); + } + // LoPreff integration public boolean isLopreffEnabled() { return config.getBoolean("lopreff.enabled", true); diff --git a/src/main/java/com/loki/lochat/gradient/config/GradientConfig.java b/src/main/java/com/loki/lochat/gradient/config/GradientConfig.java index fadba7a..9e1c7d5 100644 --- a/src/main/java/com/loki/lochat/gradient/config/GradientConfig.java +++ b/src/main/java/com/loki/lochat/gradient/config/GradientConfig.java @@ -79,4 +79,8 @@ public class GradientConfig { return getPrefixBlacklist().stream() .anyMatch(blacklisted -> lowerPrefix.contains(blacklisted.toLowerCase())); } + + // Display settings + public boolean isUpdateDisplayName() { return config.getBoolean("display.update-display-name", true); } + public boolean isUpdateTabList() { return config.getBoolean("display.update-tab-list", true); } } diff --git a/src/main/java/com/loki/lochat/gradient/listeners/GradientPlayerListener.java b/src/main/java/com/loki/lochat/gradient/listeners/GradientPlayerListener.java index 310650c..f6f23f0 100644 --- a/src/main/java/com/loki/lochat/gradient/listeners/GradientPlayerListener.java +++ b/src/main/java/com/loki/lochat/gradient/listeners/GradientPlayerListener.java @@ -32,7 +32,10 @@ public class GradientPlayerListener implements Listener { FoliaUtil.runEntityTask(module.getPlugin(), player, () -> { if (!player.isOnline()) return; - DisplayNameUtil.updateDisplayName(module, player, data); + // Обновляем display name если включено + if (module.getConfig().isUpdateDisplayName()) { + DisplayNameUtil.updateDisplayName(module, player, data); + } if (data.hasPrefix() && data.isPrefixEnabled()) { module.getLuckPermsHook().setPrefix(player, diff --git a/src/main/java/com/loki/lochat/managers/ChatColorManager.java b/src/main/java/com/loki/lochat/managers/ChatColorManager.java new file mode 100644 index 0000000..191ff4c --- /dev/null +++ b/src/main/java/com/loki/lochat/managers/ChatColorManager.java @@ -0,0 +1,93 @@ +package com.loki.lochat.managers; + +import com.loki.lochat.LoChat; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class ChatColorManager { + + private final LoChat plugin; + private final Map chatColors; + private File dataFile; + private FileConfiguration dataConfig; + + public ChatColorManager(LoChat plugin) { + this.plugin = plugin; + this.chatColors = new HashMap<>(); + loadData(); + } + + private void loadData() { + dataFile = new File(plugin.getDataFolder(), "chatcolors.yml"); + if (!dataFile.exists()) { + try { + dataFile.createNewFile(); + } catch (IOException e) { + plugin.getLogger().severe("Не удалось создать файл chatcolors.yml: " + e.getMessage()); + } + } + + dataConfig = YamlConfiguration.loadConfiguration(dataFile); + + // Загружаем цвета из файла + for (String key : dataConfig.getKeys(false)) { + try { + UUID uuid = UUID.fromString(key); + String color = dataConfig.getString(key); + if (color != null) { + chatColors.put(uuid, color); + } + } catch (IllegalArgumentException ignored) { + // Неверный UUID, пропускаем + } + } + } + + public void saveData() { + for (Map.Entry entry : chatColors.entrySet()) { + dataConfig.set(entry.getKey().toString(), entry.getValue()); + } + + try { + dataConfig.save(dataFile); + } catch (IOException e) { + plugin.getLogger().severe("Не удалось сохранить chatcolors.yml: " + e.getMessage()); + } + } + + public void setChatColor(UUID playerId, String color) { + chatColors.put(playerId, color); + saveData(); + } + + public String getChatColor(UUID playerId) { + return chatColors.get(playerId); + } + + public boolean hasChatColor(UUID playerId) { + return chatColors.containsKey(playerId); + } + + public void removeChatColor(UUID playerId) { + chatColors.remove(playerId); + dataConfig.set(playerId.toString(), null); + saveData(); + } + + /** + * Применяет цвет чата к сообщению + */ + public String applyChatColor(UUID playerId, String message) { + String color = getChatColor(playerId); + if (color != null) { + return "<" + color + ">" + message + ""; + } + return message; + } +} \ No newline at end of file diff --git a/src/main/java/com/loki/lochat/managers/CustomCommandManager.java b/src/main/java/com/loki/lochat/managers/CustomCommandManager.java new file mode 100644 index 0000000..ffad8fb --- /dev/null +++ b/src/main/java/com/loki/lochat/managers/CustomCommandManager.java @@ -0,0 +1,278 @@ +package com.loki.lochat.managers; + +import com.loki.lochat.LoChat; +import com.loki.lochat.commands.CustomCommand; +import com.loki.lochat.utils.ChatFormatter; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.title.Title; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandMap; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; + +import java.io.File; +import java.lang.reflect.Field; +import java.time.Duration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class CustomCommandManager { + + private final LoChat plugin; + private final Map commands; + private File commandsFile; + private FileConfiguration commandsConfig; + + public CustomCommandManager(LoChat plugin) { + this.plugin = plugin; + this.commands = new HashMap<>(); + loadCommands(); + } + + private void loadCommands() { + commandsFile = new File(plugin.getDataFolder(), "custom-commands.yml"); + if (!commandsFile.exists()) { + plugin.saveResource("custom-commands.yml", false); + } + + 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"), + section.getString("message", ""), + section.getStringList("aliases"), + section.getString("type", "chat"), + section.getString("target", "sender") + ); + + commands.put(commandName, data); + + // Регистрируем команду + registerCommand(data); + } + + plugin.getLogger().info("Загружено " + commands.size() + " кастомных команд"); + } + + private void registerCommand(CustomCommandData data) { + try { + // Получаем CommandMap через рефлексию + 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()); + } + } + + public void reload() { + loadCommands(); + } + + public boolean executeCustomCommand(String commandName, Player player, String[] args) { + CustomCommandData data = commands.get(commandName); + if (data == null) { + // Проверяем алиасы + for (CustomCommandData cmd : commands.values()) { + if (cmd.aliases.contains(commandName)) { + data = cmd; + break; + } + } + } + + 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 "chat" -> sendChatMessage(data.target, player, message); + case "broadcast" -> sendBroadcast(data.target, message); + case "title" -> sendTitle(data.target, player, message); + 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")) { + result = me.clip.placeholderapi.PlaceholderAPI.setPlaceholders(player, result); + } + + 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" -> { + for (Player player : Bukkit.getOnlinePlayers()) { + player.sendMessage(component); + } + } + default -> { + if (target.startsWith("permission:")) { + String permission = target.substring(11); + for (Player player : Bukkit.getOnlinePlayers()) { + if (player.hasPermission(permission)) { + player.sendMessage(component); + } + } + } else { + sender.sendMessage(component); + } + } + } + } + + private void sendBroadcast(String target, String message) { + Component component = ChatFormatter.parse(message); + + switch (target.toLowerCase()) { + case "all" -> { + for (Player player : Bukkit.getOnlinePlayers()) { + player.sendMessage(component); + } + } + default -> { + if (target.startsWith("permission:")) { + String permission = target.substring(11); + for (Player player : Bukkit.getOnlinePlayers()) { + if (player.hasPermission(permission)) { + player.sendMessage(component); + } + } + } + } + } + } + + private void sendTitle(String target, Player sender, String message) { + 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.Times.times(Duration.ofMillis(500), Duration.ofSeconds(3), Duration.ofMillis(500))); + + switch (target.toLowerCase()) { + case "sender" -> sender.showTitle(titleObj); + case "all" -> { + for (Player player : Bukkit.getOnlinePlayers()) { + player.showTitle(titleObj); + } + } + default -> { + if (target.startsWith("permission:")) { + String permission = target.substring(11); + for (Player player : Bukkit.getOnlinePlayers()) { + if (player.hasPermission(permission)) { + player.showTitle(titleObj); + } + } + } else { + sender.showTitle(titleObj); + } + } + } + } + + private void sendActionBar(String target, Player sender, String message) { + Component component = ChatFormatter.parse(message); + + switch (target.toLowerCase()) { + case "sender" -> sender.sendActionBar(component); + case "all" -> { + for (Player player : Bukkit.getOnlinePlayers()) { + player.sendActionBar(component); + } + } + default -> { + if (target.startsWith("permission:")) { + String permission = target.substring(11); + for (Player player : Bukkit.getOnlinePlayers()) { + if (player.hasPermission(permission)) { + player.sendActionBar(component); + } + } + } else { + sender.sendActionBar(component); + } + } + } + } + + public Map getCommands() { + return commands; + } + + public static class CustomCommandData { + public final String name; + public final String permission; + public final String message; + public final List aliases; + public final String type; + public final String target; + + public CustomCommandData(String name, String permission, String message, + List aliases, String type, String target) { + this.name = name; + this.permission = permission; + this.message = message; + this.aliases = aliases; + this.type = type; + this.target = target; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/loki/lochat/utils/ChatFormatter.java b/src/main/java/com/loki/lochat/utils/ChatFormatter.java index 66ca977..ee80853 100644 --- a/src/main/java/com/loki/lochat/utils/ChatFormatter.java +++ b/src/main/java/com/loki/lochat/utils/ChatFormatter.java @@ -123,7 +123,7 @@ public class ChatFormatter { /** * Полная обработка сообщения игрока - * Цвета + смайлики + * Цвета + смайлики + цвет чата */ public static String processPlayerMessage(String message, Player player, boolean hasColorPermission) { String result = message; @@ -137,6 +137,12 @@ public class ChatFormatter { } else { // Убираем цветовые коды result = stripLegacyColors(result); + + // Применяем цвет чата если установлен + LoChat plugin = LoChat.getInstance(); + if (plugin != null && plugin.getChatColorManager() != null) { + result = plugin.getChatColorManager().applyChatColor(player.getUniqueId(), result); + } } return result; @@ -235,6 +241,10 @@ public class ChatFormatter { && plugin.getGradientModule() != null && plugin.getGradientModule().isEnabled()) { senderDisplay = plugin.getGradientModule().getFormattedName(sender); receiverDisplay = plugin.getGradientModule().getFormattedName(receiver); + + // Конвертируем legacy коды в MiniMessage формат + senderDisplay = convertAllColors(senderDisplay); + receiverDisplay = convertAllColors(receiverDisplay); } return parse(format @@ -258,6 +268,10 @@ public class ChatFormatter { && plugin.getGradientModule() != null && plugin.getGradientModule().isEnabled()) { senderDisplay = plugin.getGradientModule().getFormattedName(sender); receiverDisplay = plugin.getGradientModule().getFormattedName(receiver); + + // Конвертируем legacy коды в MiniMessage формат + senderDisplay = convertAllColors(senderDisplay); + receiverDisplay = convertAllColors(receiverDisplay); } return parse(format diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index ae85b25..1f4c151 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,5 +1,8 @@ # LoChat Configuration # Author: loki +# Config Version: 2 + +config-version: 2 chat: global: @@ -67,3 +70,14 @@ automessages: formats: announcement: "[ОБЪЯВЛЕНИЕ] " + +# Настройки объявлений +announcements: + # Показывать title при объявлении + show-title: true + # Заголовок title + title-header: "ОБЪЯВЛЕНИЕ" + # Показывать actionbar при объявлении + show-actionbar: true + # Время показа title (в секундах) + title-duration: 3 diff --git a/src/main/resources/custom-commands.yml b/src/main/resources/custom-commands.yml new file mode 100644 index 0000000..571d508 --- /dev/null +++ b/src/main/resources/custom-commands.yml @@ -0,0 +1,51 @@ +# Кастомные команды LoChat +# Здесь можно создавать свои команды с сообщениями и алиасами + +# Пример структуры: +# command-name: +# permission: "lochat.command.name" # Право для использования (опционально) +# message: "Текст сообщения" # Сообщение которое отправится +# aliases: ["alias1", "alias2"] # Алиасы команды (опционально) +# type: "chat" # Тип: chat, broadcast, title, actionbar +# target: "sender" # Кому отправить: sender, all, permission:право + +# Примеры команд: +help: + message: "Добро пожаловать на сервер!\nИспользуйте /rules для просмотра правил" + aliases: ["помощь", "хелп"] + type: "chat" + target: "sender" + +rules: + message: "ПРАВИЛА СЕРВЕРА:\n1. Не гриферить\n2. Не читерить\n3. Уважать других игроков" + aliases: ["правила", "rule"] + type: "chat" + target: "sender" + +discord: + message: "Наш Discord сервер: discord.gg/example" + aliases: ["дискорд", "ds"] + type: "chat" + target: "sender" + +# Команда для админов +restart-warning: + permission: "lochat.admin" + message: "ВНИМАНИЕ! Сервер будет перезагружен через 5 минут!" + aliases: ["рестарт", "reboot"] + type: "broadcast" + target: "all" + +# Команда с title +welcome: + message: "Добро пожаловать!" + aliases: ["привет"] + type: "title" + target: "sender" + +# Команда с actionbar +coords: + message: "Ваши координаты: {x}, {y}, {z}" + aliases: ["координаты", "pos"] + type: "actionbar" + target: "sender" \ No newline at end of file diff --git a/src/main/resources/gradient-config.yml b/src/main/resources/gradient-config.yml index d2ea06b..19bf76d 100644 --- a/src/main/resources/gradient-config.yml +++ b/src/main/resources/gradient-config.yml @@ -43,6 +43,13 @@ continuous-gradient: true # Использовать &#RRGGBB формат (true) или <#RRGGBB> (false) use-legacy-rgb-format: true +# Отображение над игроком +display: + # Обновлять display name игрока (показывает префикс+ник над головой) + update-display-name: true + # Обновлять tab list + update-tab-list: true + # Тип хранилища: YAML или SQLITE storage-type: YAML diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 6358bd7..f51017c 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: LoChat -version: '1.0.0' +version: '1.5.0' main: com.loki.lochat.LoChat api-version: '1.21' folia-supported: true @@ -31,28 +31,31 @@ commands: description: Send private message usage: /msg permission: chat.pm.use - aliases: [tell, w, pm] + aliases: [tell, w, pm, m, ьып] reply: description: Reply to last message usage: /reply permission: chat.pm.use - aliases: [r] + aliases: [r, ответить] ignore: description: Ignore player usage: /ignore permission: chat.pm.ignore + aliases: [игнор] unignore: description: Unignore player usage: /unignore permission: chat.pm.ignore + aliases: [разигнор] announce: description: Send announcement usage: /announce permission: chat.announce.use + aliases: [объявление, анонс] chatspy: description: Toggle chat spy mode @@ -66,6 +69,12 @@ commands: permission: chat.clear aliases: [cc] + chatcolor: + description: Set chat color + usage: /chatcolor + permission: lochat.chatcolor + aliases: [цветчата, chatcolour] + lochat: description: LoChat admin command usage: /lochat [reload] @@ -75,12 +84,13 @@ commands: description: Set gradient nick color usage: /color [hex2] ... permission: gradient.color - aliases: [gradient] + aliases: [gradient, цвет, градиент] prefix: description: Set custom prefix usage: /prefix permission: gradient.prefix + aliases: [префикс] aprefix: description: Admin gradient commands @@ -119,6 +129,8 @@ permissions: default: op chat.mention.use: default: true + lochat.chatcolor: + default: true gradient.color: default: true gradient.prefix: