From 712a8a3353a2ad36db25d9c0cd96116b4def6c9b Mon Sep 17 00:00:00 2001 From: loki5512344 Date: Thu, 1 Jan 2026 23:40:02 +0100 Subject: [PATCH] Remove TextDisplay, display name updates, mute system, and filter/censorship --- src/main/java/com/loki/lochat/LoChat.java | 18 -- .../com/loki/lochat/commands/MuteCommand.java | 122 ------- .../loki/lochat/commands/UnmuteCommand.java | 76 ----- .../loki/lochat/gradient/GradientModule.java | 8 - .../commands/GradientAdminCommand.java | 35 +- .../gradient/config/GradientConfig.java | 41 --- .../listeners/GradientPlayerListener.java | 59 +--- .../lochat/gradient/util/DisplayNameUtil.java | 7 - .../gradient/util/TextDisplayManager.java | 165 ---------- .../integrations/PlaceholderAPIHook.java | 11 - .../loki/lochat/listeners/ChatListener.java | 50 --- .../loki/lochat/managers/FilterManager.java | 302 ------------------ .../com/loki/lochat/managers/MuteManager.java | 147 --------- src/main/resources/config.yml | 10 - src/main/resources/gradient-config.yml | 15 - 15 files changed, 2 insertions(+), 1064 deletions(-) delete mode 100644 src/main/java/com/loki/lochat/commands/MuteCommand.java delete mode 100644 src/main/java/com/loki/lochat/commands/UnmuteCommand.java delete mode 100644 src/main/java/com/loki/lochat/gradient/util/TextDisplayManager.java delete mode 100644 src/main/java/com/loki/lochat/managers/FilterManager.java delete mode 100644 src/main/java/com/loki/lochat/managers/MuteManager.java diff --git a/src/main/java/com/loki/lochat/LoChat.java b/src/main/java/com/loki/lochat/LoChat.java index 9bde434..f0e437f 100644 --- a/src/main/java/com/loki/lochat/LoChat.java +++ b/src/main/java/com/loki/lochat/LoChat.java @@ -20,14 +20,12 @@ public final class LoChat extends JavaPlugin { private PMManager pmManager; private IgnoreManager ignoreManager; private AutoMessageManager autoMessageManager; - private FilterManager filterManager; private AntiSpamManager antiSpamManager; private CooldownManager cooldownManager; private MentionManager mentionManager; private SpyManager spyManager; private EmojiManager emojiManager; private CustomCommandManager customCommandManager; - private MuteManager muteManager; private GradientModule gradientModule; @Override @@ -42,13 +40,11 @@ public final class LoChat extends JavaPlugin { emojiManager = new EmojiManager(this); ignoreManager = new IgnoreManager(this); pmManager = new PMManager(); - filterManager = new FilterManager(this); antiSpamManager = new AntiSpamManager(this); cooldownManager = new CooldownManager(); mentionManager = new MentionManager(this); spyManager = new SpyManager(this); customCommandManager = new CustomCommandManager(this); - muteManager = new MuteManager(this); chatManager = new ChatManager(this); autoMessageManager = new AutoMessageManager(this); @@ -84,9 +80,6 @@ public final class LoChat extends JavaPlugin { if (ignoreManager != null) { ignoreManager.save(); } - if (muteManager != null) { - muteManager.saveData(); - } if (gradientModule != null) { gradientModule.shutdown(); } @@ -104,8 +97,6 @@ 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("mute").setExecutor(new MuteCommand(this)); - getCommand("unmute").setExecutor(new UnmuteCommand(this)); LoChatCommand loChatCommand = new LoChatCommand(this); getCommand("lochat").setExecutor(loChatCommand); @@ -124,9 +115,6 @@ public final class LoChat extends JavaPlugin { if (customCommandManager != null) { customCommandManager.reload(); } - if (filterManager != null) { - filterManager.reload(); - } } public static LoChat getInstance() { @@ -157,9 +145,6 @@ public final class LoChat extends JavaPlugin { return autoMessageManager; } - public FilterManager getFilterManager() { - return filterManager; - } public AntiSpamManager getAntiSpamManager() { return antiSpamManager; @@ -189,7 +174,4 @@ public final class LoChat extends JavaPlugin { return customCommandManager; } - public MuteManager getMuteManager() { - return muteManager; - } } diff --git a/src/main/java/com/loki/lochat/commands/MuteCommand.java b/src/main/java/com/loki/lochat/commands/MuteCommand.java deleted file mode 100644 index e1fa1a8..0000000 --- a/src/main/java/com/loki/lochat/commands/MuteCommand.java +++ /dev/null @@ -1,122 +0,0 @@ -package com.loki.lochat.commands; - -import com.loki.lochat.LoChat; -import com.loki.lochat.utils.ChatFormatter; -import org.bukkit.Bukkit; -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; -import java.util.stream.Collectors; - -public class MuteCommand implements CommandExecutor, TabCompleter { - - private final LoChat plugin; - - public MuteCommand(LoChat plugin) { - this.plugin = plugin; - } - - @Override - public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - if (!sender.hasPermission("chat.mute")) { - sender.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getNoPermission())); - return true; - } - - if (args.length < 2) { - sender.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getInvalidUsage("/mute <игрок> <время> [причина]"))); - sender.sendMessage(ChatFormatter.parse("󅒐Время: 1m, 1h, 1d, permanent")); - return true; - } - - String playerName = args[0]; - String timeStr = args[1]; - String reason = args.length > 2 ? String.join(" ", Arrays.copyOfRange(args, 2, args.length)) : "Нарушение правил чата"; - - Player target = Bukkit.getPlayer(playerName); - if (target == null) { - sender.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getPlayerNotFound())); - return true; - } - - // Парсим время - long duration = parseTime(timeStr); - if (duration == -1) { - sender.sendMessage(ChatFormatter.parse("&#FF0000Неверный формат времени! Используйте: 1m, 1h, 1d, permanent")); - return true; - } - - // Мутим игрока - plugin.getMuteManager().mutePlayer(target.getUniqueId(), duration, reason, sender.getName()); - - // Уведомления - String timeDisplay = duration == 0 ? "навсегда" : formatTime(duration); - sender.sendMessage(ChatFormatter.parse( - plugin.getMessageConfig().get("mute.muted", "{player}", target.getName(), "{time}", timeDisplay) - )); - - if (target.isOnline()) { - target.sendMessage(ChatFormatter.parse( - plugin.getMessageConfig().get("mute.you-muted", "{time}", timeDisplay) - )); - } - - return true; - } - - private long parseTime(String timeStr) { - if (timeStr.equalsIgnoreCase("permanent") || timeStr.equalsIgnoreCase("perm")) { - return 0; // 0 = permanent - } - - try { - char unit = timeStr.charAt(timeStr.length() - 1); - int amount = Integer.parseInt(timeStr.substring(0, timeStr.length() - 1)); - - return switch (unit) { - case 'm' -> amount * 60L * 1000L; // минуты - case 'h' -> amount * 60L * 60L * 1000L; // часы - case 'd' -> amount * 24L * 60L * 60L * 1000L; // дни - default -> -1; - }; - } catch (NumberFormatException | StringIndexOutOfBoundsException e) { - return -1; - } - } - - private String formatTime(long millis) { - long seconds = millis / 1000; - long minutes = seconds / 60; - long hours = minutes / 60; - long days = hours / 24; - - if (days > 0) return days + "д"; - if (hours > 0) return hours + "ч"; - if (minutes > 0) return minutes + "м"; - return seconds + "с"; - } - - @Override - public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { - if (args.length == 1) { - return Bukkit.getOnlinePlayers().stream() - .map(Player::getName) - .filter(name -> name.toLowerCase().startsWith(args[0].toLowerCase())) - .collect(Collectors.toList()); - } else if (args.length == 2) { - List times = Arrays.asList("1m", "5m", "10m", "30m", "1h", "2h", "6h", "12h", "1d", "3d", "7d", "permanent"); - return times.stream() - .filter(time -> time.toLowerCase().startsWith(args[1].toLowerCase())) - .collect(Collectors.toList()); - } - return new ArrayList<>(); - } -} \ No newline at end of file diff --git a/src/main/java/com/loki/lochat/commands/UnmuteCommand.java b/src/main/java/com/loki/lochat/commands/UnmuteCommand.java deleted file mode 100644 index db13ac1..0000000 --- a/src/main/java/com/loki/lochat/commands/UnmuteCommand.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.loki.lochat.commands; - -import com.loki.lochat.LoChat; -import com.loki.lochat.utils.ChatFormatter; -import org.bukkit.Bukkit; -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.List; -import java.util.stream.Collectors; - -public class UnmuteCommand implements CommandExecutor, TabCompleter { - - private final LoChat plugin; - - public UnmuteCommand(LoChat plugin) { - this.plugin = plugin; - } - - @Override - public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - if (!sender.hasPermission("chat.mute")) { - sender.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getNoPermission())); - return true; - } - - if (args.length != 1) { - sender.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getInvalidUsage("/unmute <игрок>"))); - return true; - } - - String playerName = args[0]; - Player target = Bukkit.getPlayer(playerName); - if (target == null) { - sender.sendMessage(ChatFormatter.parse(plugin.getMessageConfig().getPlayerNotFound())); - return true; - } - - if (!plugin.getMuteManager().isMuted(target.getUniqueId())) { - sender.sendMessage(ChatFormatter.parse( - plugin.getMessageConfig().get("mute.not-muted", "{player}", target.getName()) - )); - return true; - } - - // Размучиваем - plugin.getMuteManager().unmutePlayer(target.getUniqueId()); - - // Уведомления - sender.sendMessage(ChatFormatter.parse( - plugin.getMessageConfig().get("mute.unmuted", "{player}", target.getName()) - )); - - if (target.isOnline()) { - target.sendMessage(ChatFormatter.parse("�FF00Вы были размучены!")); - } - - return true; - } - - @Override - public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { - if (args.length == 1) { - return Bukkit.getOnlinePlayers().stream() - .map(Player::getName) - .filter(name -> name.toLowerCase().startsWith(args[0].toLowerCase())) - .collect(Collectors.toList()); - } - return null; - } -} \ No newline at end of file diff --git a/src/main/java/com/loki/lochat/gradient/GradientModule.java b/src/main/java/com/loki/lochat/gradient/GradientModule.java index bf9499d..b508260 100644 --- a/src/main/java/com/loki/lochat/gradient/GradientModule.java +++ b/src/main/java/com/loki/lochat/gradient/GradientModule.java @@ -12,7 +12,6 @@ import com.loki.lochat.gradient.listeners.GradientGUIListener; import com.loki.lochat.gradient.listeners.GradientPlayerListener; import com.loki.lochat.gradient.util.DisplayNameUtil; import com.loki.lochat.gradient.util.GradientUtil; -import com.loki.lochat.gradient.util.TextDisplayManager; import org.black_ixx.playerpoints.PlayerPoints; import org.black_ixx.playerpoints.PlayerPointsAPI; import org.bukkit.Bukkit; @@ -30,7 +29,6 @@ public class GradientModule { private GradientDataManager dataManager; private GradientLuckPermsHook luckPermsHook; private PlayerPointsAPI playerPointsAPI; - private TextDisplayManager textDisplayManager; private boolean enabled; public GradientModule(JavaPlugin plugin) { @@ -66,8 +64,6 @@ public class GradientModule { // Подключение LuckPerms luckPermsHook = new GradientLuckPermsHook(plugin); - // Инициализация TextDisplayManager - textDisplayManager = new TextDisplayManager(this); // Регистрация команд registerCommands(); @@ -105,9 +101,6 @@ public class GradientModule { * Выключение модуля */ public void shutdown() { - if (textDisplayManager != null) { - textDisplayManager.removeAllDisplays(); - } if (dataManager != null) { dataManager.saveAll(); } @@ -268,7 +261,6 @@ public class GradientModule { public GradientDataManager getDataManager() { return dataManager; } public GradientLuckPermsHook getLuckPermsHook() { return luckPermsHook; } public PlayerPointsAPI getPlayerPointsAPI() { return playerPointsAPI; } - public TextDisplayManager getTextDisplayManager() { return textDisplayManager; } public boolean hasPlayerPoints() { return playerPointsAPI != null; } public boolean isEnabled() { return enabled; } } diff --git a/src/main/java/com/loki/lochat/gradient/commands/GradientAdminCommand.java b/src/main/java/com/loki/lochat/gradient/commands/GradientAdminCommand.java index 0b37d58..d13cd49 100644 --- a/src/main/java/com/loki/lochat/gradient/commands/GradientAdminCommand.java +++ b/src/main/java/com/loki/lochat/gradient/commands/GradientAdminCommand.java @@ -87,9 +87,6 @@ public class GradientAdminCommand implements CommandExecutor, TabCompleter { case "updateall" -> { handleUpdateAll(sender, msg); } - case "toggledisplay" -> { - handleToggleDisplay(sender, msg); - } default -> sendHelp(sender); } @@ -334,36 +331,6 @@ public class GradientAdminCommand implements CommandExecutor, TabCompleter { sender.sendMessage("§aОбновлены display names для §f" + count + " §aигроков"); } - private void handleToggleDisplay(CommandSender sender, GradientMessages msg) { - // Переключаем режим TextDisplay в конфиге - boolean currentMode = module.getConfig().isUseTextDisplay(); - boolean newMode = !currentMode; - - // Обновляем конфиг (временно в памяти) - module.getConfig().setUseTextDisplay(newMode); - - if (newMode) { - // Переключаемся на TextDisplay - sender.sendMessage("§aВключен режим TextDisplay"); - // Удаляем все обычные display names и создаем TextDisplay - for (Player player : Bukkit.getOnlinePlayers()) { - player.displayName(null); // Сбрасываем обычный display name - GradientPlayerData data = module.getDataManager().getPlayerData(player.getUniqueId()); - FoliaUtil.runEntityTask(module.getPlugin(), player, - () -> module.getTextDisplayManager().updatePlayerDisplay(player)); - } - } else { - // Переключаемся на обычный display name - sender.sendMessage("§cВыключен режим TextDisplay"); - // Удаляем все TextDisplay и обновляем обычные display names - module.getTextDisplayManager().removeAllDisplays(); - for (Player player : Bukkit.getOnlinePlayers()) { - GradientPlayerData data = module.getDataManager().getPlayerData(player.getUniqueId()); - FoliaUtil.runEntityTask(module.getPlugin(), player, - () -> DisplayNameUtil.updateDisplayName(module, player, data)); - } - } - } @Override public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @@ -372,7 +339,7 @@ public class GradientAdminCommand implements CommandExecutor, TabCompleter { if (args.length == 1) { return Arrays.asList("reload", "info", "setcolor", "setprefix", "coloron", "coloroff", - "prefixon", "prefixoff", "resetcolor", "resetprefix", "updateall", "toggledisplay").stream() + "prefixon", "prefixoff", "resetcolor", "resetprefix", "updateall").stream() .filter(s -> s.startsWith(args[0].toLowerCase())) .toList(); } 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 61c0f33..71fe716 100644 --- a/src/main/java/com/loki/lochat/gradient/config/GradientConfig.java +++ b/src/main/java/com/loki/lochat/gradient/config/GradientConfig.java @@ -83,7 +83,6 @@ public class GradientConfig { .anyMatch(blacklisted -> lowerPrefix.contains(blacklisted.toLowerCase())); } - // Display settings - читаем из main config.yml, если есть, иначе из gradient-config.yml public boolean isUpdateDisplayName() { if (mainConfig.contains("display.update-display-name")) { return mainConfig.getBoolean("display.update-display-name", true); @@ -97,44 +96,4 @@ public class GradientConfig { } return config.getBoolean("display.update-tab-list", true); } - - public boolean isUseTextDisplay() { - if (mainConfig.contains("display.use-text-display")) { - return mainConfig.getBoolean("display.use-text-display", true); - } - return config.getBoolean("display.use-text-display", true); - } - - public double getTextDisplayHeight() { - if (mainConfig.contains("display.text-display-height")) { - return mainConfig.getDouble("display.text-display-height", 2.8); - } - return config.getDouble("display.text-display-height", 2.8); - } - - public float getTextDisplayScale() { - if (mainConfig.contains("display.text-display-scale")) { - return (float) mainConfig.getDouble("display.text-display-scale", 0.8); - } - return (float) config.getDouble("display.text-display-scale", 0.8); - } - - public boolean isTextDisplaySeeThrough() { - if (mainConfig.contains("display.text-display-see-through")) { - return mainConfig.getBoolean("display.text-display-see-through", false); - } - return config.getBoolean("display.text-display-see-through", false); - } - - public String getTextDisplayFormat() { - if (mainConfig.contains("display.text-display-format")) { - return mainConfig.getString("display.text-display-format", "{prefix}{player}"); - } - return config.getString("display.text-display-format", "{prefix}{player}"); - } - - // Метод для временного изменения настройки TextDisplay - public void setUseTextDisplay(boolean useTextDisplay) { - config.set("display.use-text-display", useTextDisplay); - } } 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 1f59b47..e97aa4b 100644 --- a/src/main/java/com/loki/lochat/gradient/listeners/GradientPlayerListener.java +++ b/src/main/java/com/loki/lochat/gradient/listeners/GradientPlayerListener.java @@ -1,8 +1,6 @@ package com.loki.lochat.gradient.listeners; import com.loki.lochat.gradient.GradientModule; -import com.loki.lochat.gradient.data.GradientPlayerData; -import com.loki.lochat.gradient.util.DisplayNameUtil; import com.loki.lochat.gradient.util.FoliaUtil; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -10,7 +8,6 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.event.player.PlayerTeleportEvent; /** * Слушатель событий игроков для градиентного модуля @@ -26,69 +23,15 @@ public class GradientPlayerListener implements Listener { @EventHandler(priority = EventPriority.HIGHEST) public void onPlayerJoin(PlayerJoinEvent event) { Player player = event.getPlayer(); - FoliaUtil.runAsync(module.getPlugin(), () -> { - GradientPlayerData data = module.getDataManager().getPlayerData(player.getUniqueId()); - - FoliaUtil.runEntityTask(module.getPlugin(), player, () -> { - if (!player.isOnline()) return; - - // Обновляем display name если включено - if (module.getConfig().isUpdateDisplayName()) { - DisplayNameUtil.updateDisplayName(module, player, data); - } - - if (data.hasPrefix() && data.isPrefixEnabled()) { - module.getLuckPermsHook().setPrefix(player, - DisplayNameUtil.buildColoredPrefix(module, data)); - } - }); + module.getDataManager().getPlayerData(player.getUniqueId()); }); } @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { Player player = event.getPlayer(); - - // Удаляем TextDisplay если есть - if (module.getTextDisplayManager() != null) { - module.getTextDisplayManager().removePlayerDisplay(player.getUniqueId()); - } - FoliaUtil.runAsync(module.getPlugin(), () -> module.getDataManager().savePlayerData(player.getUniqueId())); } - - @EventHandler - public void onPlayerToggleSneak(org.bukkit.event.player.PlayerToggleSneakEvent event) { - Player player = event.getPlayer(); - - // Скрываем/показываем TextDisplay при приседании - if (module.getTextDisplayManager() != null && module.getTextDisplayManager().hasDisplay(player.getUniqueId())) { - FoliaUtil.runEntityTask(module.getPlugin(), player, () -> { - if (event.isSneaking()) { - // Игрок начал приседать - скрываем TextDisplay - module.getTextDisplayManager().hidePlayerDisplay(player.getUniqueId()); - } else { - // Игрок перестал приседать - показываем TextDisplay - module.getTextDisplayManager().showPlayerDisplay(player.getUniqueId()); - } - }); - } - } - - @EventHandler(priority = EventPriority.MONITOR) - public void onPlayerTeleport(PlayerTeleportEvent event) { - if (event.isCancelled()) return; - - Player player = event.getPlayer(); - - // Обновляем TextDisplay после телепортации - if (module.getTextDisplayManager() != null && module.getTextDisplayManager().hasDisplay(player.getUniqueId())) { - FoliaUtil.runEntityTask(module.getPlugin(), player, () -> { - if (!player.isOnline()) return; - module.getTextDisplayManager().updateDisplayPosition(player); - }); - } - } } diff --git a/src/main/java/com/loki/lochat/gradient/util/DisplayNameUtil.java b/src/main/java/com/loki/lochat/gradient/util/DisplayNameUtil.java index 7b00a08..be6ff11 100644 --- a/src/main/java/com/loki/lochat/gradient/util/DisplayNameUtil.java +++ b/src/main/java/com/loki/lochat/gradient/util/DisplayNameUtil.java @@ -25,13 +25,6 @@ public final class DisplayNameUtil { public static void updateDisplayName(GradientModule module, Player player, GradientPlayerData data) { GradientConfig cfg = module.getConfig(); - - // Если включен TextDisplay, используем его вместо обычного display name - if (cfg.isUseTextDisplay()) { - module.getTextDisplayManager().updatePlayerDisplay(player); - return; - } - String prefix = null; String prefixFormat = cfg.getPrefixFormat(); diff --git a/src/main/java/com/loki/lochat/gradient/util/TextDisplayManager.java b/src/main/java/com/loki/lochat/gradient/util/TextDisplayManager.java deleted file mode 100644 index 19989e4..0000000 --- a/src/main/java/com/loki/lochat/gradient/util/TextDisplayManager.java +++ /dev/null @@ -1,165 +0,0 @@ -package com.loki.lochat.gradient.util; - -import com.loki.lochat.gradient.GradientModule; -import com.loki.lochat.gradient.data.GradientPlayerData; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.MiniMessage; -import org.bukkit.entity.Player; -import org.bukkit.entity.TextDisplay; -import org.bukkit.util.Transformation; -import org.joml.AxisAngle4f; -import org.joml.Vector3f; - -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - -public class TextDisplayManager { - - private final GradientModule module; - private final Map playerDisplays = new ConcurrentHashMap<>(); - private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage(); - - public TextDisplayManager(GradientModule module) { - this.module = module; - } - public void updatePlayerDisplay(Player player) { - if (!module.getConfig().isUpdateDisplayName() || !module.getConfig().isUseTextDisplay()) { - return; - } - - GradientPlayerData data = module.getDataManager().getPlayerData(player.getUniqueId()); - String displayText = getDisplayText(player, data); - Component textComponent = MINI_MESSAGE.deserialize(displayText); - - TextDisplay display = playerDisplays.get(player.getUniqueId()); - - if (display == null || !display.isValid()) { - createTextDisplay(player, textComponent); - } else { - updateTextDisplay(display, player, textComponent); - } - } - private void createTextDisplay(Player player, Component textComponent) { - removePlayerDisplay(player.getUniqueId()); - - double height = module.getConfig().getTextDisplayHeight(); - float scale = module.getConfig().getTextDisplayScale(); - boolean seeThrough = module.getConfig().isTextDisplaySeeThrough(); - - TextDisplay display = player.getWorld().spawn( - player.getLocation().add(0, height, 0), - TextDisplay.class, - entity -> { - entity.text(textComponent); - entity.setBillboard(org.bukkit.entity.Display.Billboard.CENTER); - entity.setViewRange(64.0f); - entity.setSeeThrough(seeThrough); - entity.setShadowRadius(0.0f); - entity.setShadowStrength(0.0f); - entity.setTransformation(new Transformation( - new Vector3f(0, 0, 0), - new AxisAngle4f(0, 0, 0, 1), - new Vector3f(scale, scale, scale), - new AxisAngle4f(0, 0, 0, 1) - )); - entity.setVisibleByDefault(true); - } - ); - - player.addPassenger(display); - playerDisplays.put(player.getUniqueId(), display); - } - private void updateTextDisplay(TextDisplay display, Player player, Component textComponent) { - display.text(textComponent); - if (!player.getPassengers().contains(display)) { - player.addPassenger(display); - } - } - private String getDisplayText(Player player, GradientPlayerData data) { - String format = module.getConfig().getTextDisplayFormat(); - String prefix = getPlayerPrefix(player, data); - String playerName = getPlayerName(player, data); - String result = format - .replace("{prefix}", prefix != null ? prefix : "") - .replace("{player}", playerName) - .replace("{name}", playerName); - return convertLegacyFormats(result); - } - private String convertLegacyFormats(String message) { - if (message == null) return ""; - message = message.replaceAll("&#([0-9a-fA-F]{6})", "<#$1>"); - message = message.replaceAll("(?)", "<#$1>"); - return message; - } - private String getPlayerPrefix(Player player, GradientPlayerData data) { - String prefixFormat = module.getConfig().getPrefixFormat(); - if (data.isPrefixEnabled() && data.hasPrefix()) { - String prefix = prefixFormat.replace("{prefix}", data.getPrefix()); - if (data.isColorEnabled() && data.hasColors() && module.getConfig().isGradientOnPrefix()) { - return GradientUtil.applyGradient(prefix, data.getColors(), false); - } - return prefix; - } else if (module.getLuckPermsHook().isEnabled()) { - String lpPrefix = module.getLuckPermsHook().getActivePrefix(player); - if (lpPrefix != null && !lpPrefix.isEmpty()) { - if (data.isColorEnabled() && data.hasColors() && module.getConfig().isGradientOnLuckPermsPrefix()) { - String cleanPrefix = stripColors(lpPrefix); - return GradientUtil.applyGradient(cleanPrefix, data.getColors(), false); - } - return lpPrefix; - } - } - return ""; - } - private String getPlayerName(Player player, GradientPlayerData data) { - if (data.isColorEnabled() && data.hasColors()) { - return GradientUtil.applyGradient(player.getName(), data.getColors(), false); - } - return player.getName(); - } - private String stripColors(String text) { - if (text == null) return ""; - return text.replaceAll("(?i)(┬зx(┬з[0-9a-f]){6}|┬з[0-9a-fk-or]|&[0-9a-fk-or]|&#[0-9a-f]{6}|<[^>]+>)", ""); - } - public void removePlayerDisplay(UUID playerId) { - TextDisplay display = playerDisplays.remove(playerId); - if (display != null && display.isValid()) { - display.remove(); - } - } - public void removeAllDisplays() { - for (TextDisplay display : playerDisplays.values()) { - if (display != null && display.isValid()) { - display.remove(); - } - } - playerDisplays.clear(); - } - public void updateDisplayPosition(Player player) { - TextDisplay display = playerDisplays.get(player.getUniqueId()); - if (display != null && display.isValid()) { - if (!player.getPassengers().contains(display)) { - player.addPassenger(display); - } - } - } - public boolean hasDisplay(UUID playerId) { - TextDisplay display = playerDisplays.get(playerId); - return display != null && display.isValid(); - } - - public void hidePlayerDisplay(UUID playerId) { - TextDisplay display = playerDisplays.get(playerId); - if (display != null && display.isValid()) { - display.setViewRange(0.0f); - } - } - - public void showPlayerDisplay(UUID playerId) { - TextDisplay display = playerDisplays.get(playerId); - if (display != null && display.isValid()) { - display.setViewRange(64.0f); - } - } -} diff --git a/src/main/java/com/loki/lochat/integrations/PlaceholderAPIHook.java b/src/main/java/com/loki/lochat/integrations/PlaceholderAPIHook.java index 09d9563..23b5f58 100644 --- a/src/main/java/com/loki/lochat/integrations/PlaceholderAPIHook.java +++ b/src/main/java/com/loki/lochat/integrations/PlaceholderAPIHook.java @@ -57,17 +57,6 @@ public class PlaceholderAPIHook extends PlaceholderExpansion { } case "spy_enabled" -> String.valueOf(plugin.getSpyManager().isSpying(uuid)); - // Mute placeholders - case "muted" -> String.valueOf(plugin.getMuteManager().isMuted(uuid)); - case "mute_time" -> { - if (!plugin.getMuteManager().isMuted(uuid)) yield ""; - yield plugin.getMuteManager().formatRemainingTime(uuid); - } - case "mute_reason" -> { - var muteData = plugin.getMuteManager().getMuteData(uuid); - yield muteData != null ? muteData.reason : ""; - } - // Gradient placeholders (совместимость с LoPreff) // Используем TAB формат §x§R§R§G§G§B§B для совместимости с TAB плагином case "gradient_full", "full" -> { diff --git a/src/main/java/com/loki/lochat/listeners/ChatListener.java b/src/main/java/com/loki/lochat/listeners/ChatListener.java index 8d54d2b..b18bd22 100644 --- a/src/main/java/com/loki/lochat/listeners/ChatListener.java +++ b/src/main/java/com/loki/lochat/listeners/ChatListener.java @@ -2,7 +2,6 @@ package com.loki.lochat.listeners; import com.loki.lochat.LoChat; import com.loki.lochat.managers.AntiSpamManager; -import com.loki.lochat.managers.FilterManager; import io.papermc.paper.event.player.AsyncChatEvent; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextReplacementConfig; @@ -13,8 +12,6 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerQuitEvent; -import java.util.regex.Pattern; - public class ChatListener implements Listener { private final LoChat plugin; @@ -40,25 +37,6 @@ public class ChatListener implements Listener { } private void processChat(Player player, Component message, String plainMessage) { - - // ===== MUTE ===== - if (plugin.getMuteManager().isMuted(player.getUniqueId())) { - String timeLeft = plugin.getMuteManager() - .formatRemainingTime(player.getUniqueId()); - - if (timeLeft.equals("навсегда")) { - player.sendMessage( - plugin.getMessageConfig().getComponent("mute.permanent") - ); - } else { - player.sendMessage( - plugin.getMessageConfig() - .getComponent("mute.you-muted", "{time}", timeLeft) - ); - } - return; - } - // ===== GLOBAL / LOCAL ===== String globalSymbol = plugin.getConfigManager().getGlobalSymbol(); boolean isGlobal = plainMessage.startsWith(globalSymbol) @@ -125,27 +103,6 @@ public class ChatListener implements Listener { } } - // ===== FILTER ===== - if (!player.hasPermission("chat.bypass.filter")) { - FilterManager.FilterResult filter = - plugin.getFilterManager().checkMessage(processedPlain); - - switch (filter) { - case BLOCKED -> { - player.sendMessage( - plugin.getMessageConfig().getComponent("filter.blocked") - ); - return; - } - case WARNED -> { - player.sendMessage( - plugin.getMessageConfig().getComponent("filter.warned") - ); - processedMessage = censor(processedMessage); - } - case CENSORED -> processedMessage = censor(processedMessage); - } - } plugin.getCooldownManager().setCooldown(player.getUniqueId(), chatType); @@ -157,13 +114,6 @@ public class ChatListener implements Listener { } } - private Component censor(Component component) { - return component.replaceText(TextReplacementConfig.builder() - .match(Pattern.compile("(?i)badword")) - .replacement("***") - .build()); - } - @EventHandler public void onQuit(PlayerQuitEvent event) { Player player = event.getPlayer(); diff --git a/src/main/java/com/loki/lochat/managers/FilterManager.java b/src/main/java/com/loki/lochat/managers/FilterManager.java deleted file mode 100644 index b20eb3f..0000000 --- a/src/main/java/com/loki/lochat/managers/FilterManager.java +++ /dev/null @@ -1,302 +0,0 @@ -package com.loki.lochat.managers; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.loki.lochat.LoChat; - -import java.io.File; -import java.io.FileReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Pattern; - -public class FilterManager { - - private final LoChat plugin; - private List badWords = new ArrayList<>(); - private List fragments = new ArrayList<>(); - - public FilterManager(LoChat plugin) { - this.plugin = plugin; - loadFilterWords(); - } - - /** - * Загружает плохие слова из JSON файла - */ - private void loadFilterWords() { - badWords.clear(); - fragments.clear(); - - try { - // Сначала пытаемся загрузить из data folder (если есть кастомный) - File customFile = new File(plugin.getDataFolder(), "filter-words.json"); - if (customFile.exists()) { - try (FileReader reader = new FileReader(customFile, StandardCharsets.UTF_8)) { - JsonObject json = JsonParser.parseReader(reader).getAsJsonObject(); - loadFromJsonObject(json); - plugin.getLogger().info("Загружены плохие слова из filter-words.json (data folder): " + badWords.size() + " слов, " + fragments.size() + " фрагментов"); - return; - } - } - - // Если кастомного нет, копируем из ресурсов - plugin.saveResource("filter-words.json", false); - - // Теперь загружаем из data folder - if (customFile.exists()) { - try (FileReader reader = new FileReader(customFile, StandardCharsets.UTF_8)) { - JsonObject json = JsonParser.parseReader(reader).getAsJsonObject(); - loadFromJsonObject(json); - plugin.getLogger().info("Загружено " + badWords.size() + " плохих слов и " + fragments.size() + " фрагментов из filter-words.json"); - return; - } - } - - // Fallback на старый способ из config.yml - List configWords = plugin.getConfigManager().getFilterWords(); - if (configWords != null && !configWords.isEmpty()) { - badWords.addAll(configWords); - plugin.getLogger().warning("Используются слова из config.yml (filter-words.json не найден)"); - } - } catch (Exception e) { - plugin.getLogger().severe("Ошибка загрузки filter-words.json: " + e.getMessage()); - e.printStackTrace(); - // Fallback на старый способ - List configWords = plugin.getConfigManager().getFilterWords(); - if (configWords != null && !configWords.isEmpty()) { - badWords.addAll(configWords); - plugin.getLogger().info("Используются слова из config.yml (fallback)"); - } - } - } - - /** - * Загружает данные из JSON объекта - */ - private void loadFromJsonObject(JsonObject json) { - if (json.has("words")) { - JsonArray wordsArray = json.getAsJsonArray("words"); - for (JsonElement element : wordsArray) { - String word = element.getAsString(); - if (word != null && !word.trim().isEmpty()) { - badWords.add(word.trim().toLowerCase()); - } - } - } - - if (json.has("fragments")) { - JsonArray fragmentsArray = json.getAsJsonArray("fragments"); - for (JsonElement element : fragmentsArray) { - String fragment = element.getAsString(); - if (fragment != null && !fragment.trim().isEmpty()) { - fragments.add(fragment.trim().toLowerCase()); - } - } - } - } - - /** - * Перезагружает список плохих слов - */ - public void reload() { - loadFilterWords(); - } - - public enum FilterResult { - ALLOWED, - CENSORED, - BLOCKED, - WARNED - } - - /** - * Проверяет сообщение на наличие запрещенных слов - */ - public FilterResult checkMessage(String message) { - if (!plugin.getConfigManager().isFilterEnabled()) { - return FilterResult.ALLOWED; - } - - if (message == null || message.trim().isEmpty()) { - return FilterResult.ALLOWED; - } - - if (badWords.isEmpty() && fragments.isEmpty()) { - return FilterResult.ALLOWED; - } - - // Нормализуем сообщение (убираем обход фильтра) - String normalizedMessage = normalizeMessage(message); - String lowerMessage = normalizedMessage.toLowerCase(); - String originalLower = message.toLowerCase(); - - // Проверяем полные слова - for (String word : badWords) { - if (word == null || word.trim().isEmpty()) { - continue; - } - - String lowerWord = word.toLowerCase().trim(); - - // Проверяем точное совпадение слова (с границами слов) - Pattern wordPattern = Pattern.compile("\\b" + Pattern.quote(lowerWord) + "\\b", Pattern.CASE_INSENSITIVE); - if (wordPattern.matcher(normalizedMessage).find() || wordPattern.matcher(originalLower).find()) { - return getFilterResult(); - } - - // Также проверяем как подстроку - if (lowerMessage.contains(lowerWord) || originalLower.contains(lowerWord)) { - return getFilterResult(); - } - } - - // Проверяем фрагменты (поиск по фрагментам типа "пизд" найдет "пизда", "пиздец" и т.д.) - for (String fragment : fragments) { - if (fragment == null || fragment.trim().isEmpty()) { - continue; - } - - String lowerFragment = fragment.toLowerCase().trim(); - - // Ищем фрагмент в нормализованном сообщении - if (lowerMessage.contains(lowerFragment) || originalLower.contains(lowerFragment)) { - return getFilterResult(); - } - } - - return FilterResult.ALLOWED; - } - - /** - * Возвращает результат фильтрации на основе настроек - */ - private FilterResult getFilterResult() { - String action = plugin.getConfigManager().getFilterAction(); - return switch (action.toLowerCase()) { - case "block" -> FilterResult.BLOCKED; - case "warn" -> FilterResult.WARNED; - default -> FilterResult.CENSORED; - }; - } - - /** - * Цензурирует сообщение, заменяя запрещенные слова - */ - public String censorMessage(String message) { - if (!plugin.getConfigManager().isFilterEnabled()) { - return message; - } - - if (message == null || message.trim().isEmpty()) { - return message; - } - - if (badWords.isEmpty() && fragments.isEmpty()) { - return message; - } - - String replacement = plugin.getConfigManager().getFilterReplacement(); - if (replacement == null) { - replacement = "***"; - } - - String result = message; - String normalizedMessage = normalizeMessage(message); - - // Заменяем полные слова - for (String word : badWords) { - if (word == null || word.trim().isEmpty()) { - continue; - } - - // Заменяем точные совпадения слов - Pattern wordPattern = Pattern.compile("\\b" + Pattern.quote(word) + "\\b", Pattern.CASE_INSENSITIVE); - result = wordPattern.matcher(result).replaceAll(replacement); - - // Также заменяем как подстроку - Pattern substringPattern = Pattern.compile("(?i)" + Pattern.quote(word)); - result = substringPattern.matcher(result).replaceAll(replacement); - } - - // Заменяем фрагменты (находим слова содержащие фрагмент и заменяем их) - for (String fragment : fragments) { - if (fragment == null || fragment.trim().isEmpty()) { - continue; - } - - // Ищем слова содержащие фрагмент и заменяем весь фрагмент - Pattern fragmentPattern = Pattern.compile("(?i)" + Pattern.quote(fragment)); - result = fragmentPattern.matcher(result).replaceAll(replacement); - - // Также заменяем слова начинающиеся с фрагмента - Pattern wordStartPattern = Pattern.compile("\\b" + Pattern.quote(fragment) + "\\w*", Pattern.CASE_INSENSITIVE); - result = wordStartPattern.matcher(result).replaceAll(replacement); - } - - return result; - } - - /** - * Нормализует сообщение, убирая попытки обхода фильтра - * Заменяет специальные символы на обычные буквы - * Например: "пизд@" -> "пизда", "х*й" -> "хай" (но "хуй" найдет) - */ - private String normalizeMessage(String message) { - if (message == null) { - return ""; - } - - // Заменяем символы обхода на буквы (для обнаружения х*й, х_уй, пизд@ и т.д.) - String normalized = message; - - // Заменяем символы обхода на похожие буквы - normalized = normalized.replace('*', 'а'); - normalized = normalized.replace('_', 'а'); - normalized = normalized.replace('-', 'а'); - normalized = normalized.replace('.', 'а'); - normalized = normalized.replace(',', 'а'); - normalized = normalized.replace('|', 'и'); - normalized = normalized.replace('1', 'и'); - normalized = normalized.replace('!', 'и'); - normalized = normalized.replace('0', 'о'); - normalized = normalized.replace('3', 'з'); - normalized = normalized.replace('4', 'ч'); - normalized = normalized.replace('5', 'с'); - normalized = normalized.replace('6', 'б'); - normalized = normalized.replace('7', 'т'); - normalized = normalized.replace('8', 'в'); - normalized = normalized.replace('9', 'д'); - normalized = normalized.replace('@', 'а'); // пизд@ -> пизда - normalized = normalized.replace('#', 'х'); - normalized = normalized.replace('$', 'с'); - normalized = normalized.replace('%', 'о'); - normalized = normalized.replace('^', 'х'); - normalized = normalized.replace('&', 'а'); - normalized = normalized.replace('~', 'а'); - normalized = normalized.replace('`', 'а'); - normalized = normalized.replace('=', 'а'); - normalized = normalized.replace('+', 'а'); - normalized = normalized.replace('[', 'а'); - normalized = normalized.replace(']', 'а'); - normalized = normalized.replace('{', 'а'); - normalized = normalized.replace('}', 'а'); - normalized = normalized.replace('(', 'а'); - normalized = normalized.replace(')', 'а'); - normalized = normalized.replace('/', 'а'); - normalized = normalized.replace('\\', 'а'); - normalized = normalized.replace('?', 'а'); - normalized = normalized.replace('<', 'а'); - normalized = normalized.replace('>', 'а'); - - // Убираем множественные пробелы - normalized = normalized.replaceAll("\\s+", " "); - - return normalized; - } -} diff --git a/src/main/java/com/loki/lochat/managers/MuteManager.java b/src/main/java/com/loki/lochat/managers/MuteManager.java deleted file mode 100644 index 39b9e61..0000000 --- a/src/main/java/com/loki/lochat/managers/MuteManager.java +++ /dev/null @@ -1,147 +0,0 @@ -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 MuteManager { - - private final LoChat plugin; - private final Map mutedPlayers; - private File dataFile; - private FileConfiguration dataConfig; - - public MuteManager(LoChat plugin) { - this.plugin = plugin; - this.mutedPlayers = new HashMap<>(); - loadData(); - } - - private void loadData() { - dataFile = new File(plugin.getDataFolder(), "mutes.yml"); - if (!dataFile.exists()) { - try { - dataFile.createNewFile(); - } catch (IOException e) { - plugin.getLogger().severe("Не удалось создать файл mutes.yml: " + e.getMessage()); - } - } - - dataConfig = YamlConfiguration.loadConfiguration(dataFile); - - // Загружаем муты из файла - for (String key : dataConfig.getKeys(false)) { - try { - UUID uuid = UUID.fromString(key); - long expiry = dataConfig.getLong(key + ".expiry"); - String reason = dataConfig.getString(key + ".reason", "Нарушение правил"); - String mutedBy = dataConfig.getString(key + ".muted_by", "Система"); - - // Проверяем не истёк ли мут - if (expiry > 0 && System.currentTimeMillis() > expiry) { - dataConfig.set(key, null); - continue; - } - - mutedPlayers.put(uuid, new MuteData(expiry, reason, mutedBy)); - } catch (IllegalArgumentException ignored) { - // Неверный UUID, пропускаем - } - } - - saveData(); - } - - public void saveData() { - for (Map.Entry entry : mutedPlayers.entrySet()) { - String key = entry.getKey().toString(); - MuteData data = entry.getValue(); - - dataConfig.set(key + ".expiry", data.expiry); - dataConfig.set(key + ".reason", data.reason); - dataConfig.set(key + ".muted_by", data.mutedBy); - } - - try { - dataConfig.save(dataFile); - } catch (IOException e) { - plugin.getLogger().severe("Не удалось сохранить mutes.yml: " + e.getMessage()); - } - } - - public void mutePlayer(UUID playerId, long duration, String reason, String mutedBy) { - long expiry = duration == 0 ? 0 : System.currentTimeMillis() + duration; - mutedPlayers.put(playerId, new MuteData(expiry, reason, mutedBy)); - saveData(); - } - - public void unmutePlayer(UUID playerId) { - mutedPlayers.remove(playerId); - dataConfig.set(playerId.toString(), null); - saveData(); - } - - public boolean isMuted(UUID playerId) { - MuteData data = mutedPlayers.get(playerId); - if (data == null) return false; - - // Проверяем не истёк ли мут - if (data.expiry > 0 && System.currentTimeMillis() > data.expiry) { - unmutePlayer(playerId); - return false; - } - - return true; - } - - public MuteData getMuteData(UUID playerId) { - return mutedPlayers.get(playerId); - } - - public long getRemainingTime(UUID playerId) { - MuteData data = mutedPlayers.get(playerId); - if (data == null || data.expiry == 0) return 0; - - long remaining = data.expiry - System.currentTimeMillis(); - return Math.max(0, remaining); - } - - public String formatRemainingTime(UUID playerId) { - long remaining = getRemainingTime(playerId); - if (remaining == 0) { - MuteData data = getMuteData(playerId); - if (data != null && data.expiry == 0) { - return "навсегда"; - } - return "0с"; - } - - long seconds = remaining / 1000; - long minutes = seconds / 60; - long hours = minutes / 60; - long days = hours / 24; - - if (days > 0) return days + "д " + (hours % 24) + "ч"; - if (hours > 0) return hours + "ч " + (minutes % 60) + "м"; - if (minutes > 0) return minutes + "м " + (seconds % 60) + "с"; - return seconds + "с"; - } - - public static class MuteData { - public final long expiry; // 0 = permanent - public final String reason; - public final String mutedBy; - - public MuteData(long expiry, String reason, String mutedBy) { - this.expiry = expiry; - this.reason = reason; - this.mutedBy = mutedBy; - } - } -} \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 5712703..447c00c 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -40,16 +40,6 @@ display: update-display-name: true # Обновлять tab list update-tab-list: true - # Использовать TextDisplay вместо обычного display name (1.19.4+) - use-text-display: true - # Высота TextDisplay над игроком (в блоках) - text-display-height: 2.8 - # Размер TextDisplay (от 0.1 до 2.0) - text-display-scale: 0.8 - # Видно ли TextDisplay через стены - text-display-see-through: false - # Формат TextDisplay (поддерживает &b&l и другие коды, &#RRGGBB, #RRGGBB) - text-display-format: "{prefix}{player}" # Упоминания @ник mentions: diff --git a/src/main/resources/gradient-config.yml b/src/main/resources/gradient-config.yml index 58a40d6..344091b 100644 --- a/src/main/resources/gradient-config.yml +++ b/src/main/resources/gradient-config.yml @@ -49,21 +49,6 @@ display: update-display-name: true # Обновлять tab list update-tab-list: true - # Использовать TextDisplay вместо обычного display name (1.19.4+) - use-text-display: true - # Высота TextDisplay над игроком (в блоках) - text-display-height: 2.8 - # Размер TextDisplay (от 0.1 до 2.0) - text-display-scale: 0.8 - # Видно ли TextDisplay через стены - text-display-see-through: false - # Формат TextDisplay (поддерживает плейсхолдеры и различные форматы цветов) - # Поддерживаемые форматы: - # <#RRGGBB> - MiniMessage HEX - # &#RRGGBB - Legacy HEX - # #RRGGBB - Простой HEX - # , и т.д. - именованные цвета - text-display-format: "{prefix}{player}" # Тип хранилища: YAML или SQLITE storage-type: YAML