diff --git a/build.gradle b/build.gradle index fe43de9..5ccbe41 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { } group = 'com.loki' -version = '1.5.4' +version = '1.5.5' repositories { mavenCentral() @@ -43,7 +43,7 @@ test { } jar { - archiveFileName = 'LoChat-1.5.4.jar' + archiveFileName = 'LoChat-1.5.5.jar' from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } 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 e6b6e6a..61c0f33 100644 --- a/src/main/java/com/loki/lochat/gradient/config/GradientConfig.java +++ b/src/main/java/com/loki/lochat/gradient/config/GradientConfig.java @@ -18,10 +18,12 @@ public class GradientConfig { private final JavaPlugin plugin; private FileConfiguration config; + private FileConfiguration mainConfig; private File configFile; public GradientConfig(JavaPlugin plugin) { this.plugin = plugin; + this.mainConfig = plugin.getConfig(); loadConfig(); } @@ -42,6 +44,7 @@ public class GradientConfig { } public void reload() { + this.mainConfig = plugin.getConfig(); loadConfig(); } @@ -80,14 +83,55 @@ public class GradientConfig { .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); } - public boolean isUseTextDisplay() { return config.getBoolean("display.use-text-display", true); } - public double getTextDisplayHeight() { return config.getDouble("display.text-display-height", 2.8); } - public float getTextDisplayScale() { return (float) config.getDouble("display.text-display-scale", 0.8); } - public boolean isTextDisplaySeeThrough() { return config.getBoolean("display.text-display-see-through", false); } - public String getTextDisplayFormat() { return config.getString("display.text-display-format", "{prefix}{player}"); } + // 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); + } + return config.getBoolean("display.update-display-name", true); + } + + 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); + } + + 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) { 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 a4e4824..1f59b47 100644 --- a/src/main/java/com/loki/lochat/gradient/listeners/GradientPlayerListener.java +++ b/src/main/java/com/loki/lochat/gradient/listeners/GradientPlayerListener.java @@ -10,6 +10,7 @@ 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; /** * Слушатель событий игроков для градиентного модуля @@ -62,9 +63,30 @@ public class GradientPlayerListener implements Listener { public void onPlayerToggleSneak(org.bukkit.event.player.PlayerToggleSneakEvent event) { Player player = event.getPlayer(); - // Обновляем TextDisplay чтобы он не скрывался при приседании + // Скрываем/показываем 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/TextDisplayManager.java b/src/main/java/com/loki/lochat/gradient/util/TextDisplayManager.java index 96dbe64..e69de29 100644 --- a/src/main/java/com/loki/lochat/gradient/util/TextDisplayManager.java +++ b/src/main/java/com/loki/lochat/gradient/util/TextDisplayManager.java @@ -1,236 +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; - -/** - * Менеджер для управления TextDisplay над игроками - */ -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; - } - - /** - * Создает или обновляет TextDisplay над игроком - */ - 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()) { - // Создаем новый TextDisplay - createTextDisplay(player, textComponent); - } else { - // Обновляем существующий - updateTextDisplay(display, player, textComponent); - } - } - - /** - * Создает новый TextDisplay над игроком - */ - private void createTextDisplay(Player player, Component textComponent) { - // Удаляем старый display если есть - removePlayerDisplay(player.getUniqueId()); - - double height = module.getConfig().getTextDisplayHeight(); - float scale = module.getConfig().getTextDisplayScale(); - boolean seeThrough = module.getConfig().isTextDisplaySeeThrough(); - - // Создаем новый TextDisplay - 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); // Видимость на 64 блока - 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); - } - ); - - // Привязываем TextDisplay к игроку как пассажира - player.addPassenger(display); - - // Сохраняем в карте - playerDisplays.put(player.getUniqueId(), display); - } - - /** - * Обновляет существующий TextDisplay - */ - private void updateTextDisplay(TextDisplay display, Player player, Component textComponent) { - display.text(textComponent); - - // Проверяем, что display все еще привязан к игроку - 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); // Альтернативный плейсхолдер - - // Конвертируем legacy форматы в MiniMessage - return convertLegacyFormats(result); - } - - /** - * Конвертирует legacy форматы цветов в MiniMessage формат - */ - private String convertLegacyFormats(String message) { - if (message == null) return ""; - - // Конвертируем &#RRGGBB в <#RRGGBB> - message = message.replaceAll("&#([0-9a-fA-F]{6})", "<#$1>"); - - // Конвертируем #RRGGBB в <#RRGGBB> (только если не внутри тегов) - message = message.replaceAll("(?)", "<#$1>"); - - return message; - } - - /** - * Получает префикс игрока - */ - private String getPlayerPrefix(Player player, GradientPlayerData data) { - String prefixFormat = module.getConfig().getPrefixFormat(); - - // Определяем префикс: сначала кастомный, потом LuckPerms - 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); // MiniMessage формат - } - return prefix; - } else if (module.getLuckPermsHook().isEnabled()) { - String lpPrefix = module.getLuckPermsHook().getActivePrefix(player); - if (lpPrefix != null && !lpPrefix.isEmpty()) { - // Для LuckPerms префикса применяем градиент только если включено - 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); // MiniMessage формат - } - 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}|<[^>]+>)", ""); - } - - /** - * Удаляет TextDisplay игрока - */ - public void removePlayerDisplay(UUID playerId) { - TextDisplay display = playerDisplays.remove(playerId); - if (display != null && display.isValid()) { - display.remove(); - } - } - - /** - * Удаляет все TextDisplay - */ - public void removeAllDisplays() { - for (TextDisplay display : playerDisplays.values()) { - if (display != null && display.isValid()) { - display.remove(); - } - } - playerDisplays.clear(); - } - - /** - * Обновляет позицию TextDisplay (вызывается при движении игрока) - */ - public void updateDisplayPosition(Player player) { - TextDisplay display = playerDisplays.get(player.getUniqueId()); - if (display != null && display.isValid()) { - // TextDisplay автоматически следует за игроком как пассажир - // Но можем обновить высоту если нужно - if (!player.getPassengers().contains(display)) { - player.addPassenger(display); - } - } - } - - /** - * Проверяет, есть ли у игрока TextDisplay - */ - public boolean hasDisplay(UUID playerId) { - TextDisplay display = playerDisplays.get(playerId); - return display != null && display.isValid(); - } -} \ 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 9b7f4f3..1632e7a 100644 --- a/src/main/java/com/loki/lochat/utils/ChatFormatter.java +++ b/src/main/java/com/loki/lochat/utils/ChatFormatter.java @@ -45,6 +45,32 @@ public final class ChatFormatter { // Конвертируем #RRGGBB в <#RRGGBB> (только если не внутри тегов) message = message.replaceAll("(?)", "<#$1>"); + // Конвертируем &[0-9a-fk-or] в соответствующие MiniMessage теги + message = message.replaceAll("&0", ""); + message = message.replaceAll("&1", ""); + message = message.replaceAll("&2", ""); + message = message.replaceAll("&3", ""); + message = message.replaceAll("&4", ""); + message = message.replaceAll("&5", ""); + message = message.replaceAll("&6", ""); + message = message.replaceAll("&7", ""); + message = message.replaceAll("&8", ""); + message = message.replaceAll("&9", ""); + message = message.replaceAll("&a", ""); + message = message.replaceAll("&b", ""); + message = message.replaceAll("&c", ""); + message = message.replaceAll("&d", ""); + message = message.replaceAll("&e", ""); + message = message.replaceAll("&f", ""); + + // Конвертируем форматирование + message = message.replaceAll("&k", ""); + message = message.replaceAll("&l", ""); + message = message.replaceAll("&m", ""); + message = message.replaceAll("&n", ""); + message = message.replaceAll("&o", ""); + message = message.replaceAll("&r", ""); + return message; } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 5a692e4..5712703 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -34,6 +34,23 @@ lopreff: # Использовать кастомный префикс use-custom-prefix: true +# Настройки отображения ника +display: + # Обновлять display name игрока (показывает префикс+ник над головой) + 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: enabled: true diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 00f7bbe..6b533c1 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: LoChat -version: '1.5.4' +version: '1.5.5' main: com.loki.lochat.LoChat api-version: '1.21' folia-supported: true