From f2be6d6ece879be7c87bd7262325cc8fafa3d141 Mon Sep 17 00:00:00 2001 From: loki5512344 Date: Mon, 1 Jun 2026 11:38:53 +0200 Subject: [PATCH] feat: add example mine config, &#RRGGBB color support, up to 100 selection points --- mines/example_mine.yml | 182 +++++++++++++++ .../loader/region/RegionConfigLoader.java | 62 ++++-- .../loki/lomines/data/config/ui/UIConfig.java | 5 +- .../integration/hologram/HologramManager.java | 12 +- .../loki/lomines/util/format/ColorUtils.java | 207 ++++++++++++++++++ todo.md | 5 + 6 files changed, 445 insertions(+), 28 deletions(-) create mode 100644 mines/example_mine.yml create mode 100644 src/main/java/dev/loki/lomines/util/format/ColorUtils.java diff --git a/mines/example_mine.yml b/mines/example_mine.yml new file mode 100644 index 0000000..72ff312 --- /dev/null +++ b/mines/example_mine.yml @@ -0,0 +1,182 @@ +# ============================================ +# LoMines - Пример конфигурации шахты +# ============================================ + +# Название шахты (для отображения в сообщениях и голограммах) +# Поддерживает цвета в формате &#RRGGBB (HEX) +# Пример: &#FF0000Красная �FF00шахта +name: "&#FFD700Золотая шахта&#FFFFFF" + +# ============================================ +# РЕГИОНЫ (области спавна блоков) +# ============================================ +# Можно указать несколько регионов (до 100) +# Каждый регион = пара точек (от угла до угла) +# Все регионы должны быть в ОДНОМ мире +# +# Формат: мир;x;y;z;yaw;pitch +# yaw/pitch можно опустить (будет 0;0) + +region: + selection: + '1': "world;-100.0;64.0;-100.0" + '2': "world;-90.0;74.0;-90.0" + # Можно добавить больше регионов: + # '3': "world;-80.0;64.0;-100.0" + # '4': "world;-70.0;74.0;-90.0" + +# ============================================ +# БЛОКИ И ШАНСЫ +# ============================================ +# Шансы суммируются и нормализуются автоматически +# Можно использовать материалы из Minecraft +# Для Oraxen: oraxen:id_предмета +# Для ItemsAdder: ia:id_предмета + +blocks: + contents: + stone: 50.0 + coal_ore: 20.0 + iron_ore: 15.0 + gold_ore: 10.0 + diamond_ore: 4.0 + emerald_ore: 1.0 + + # Режим заполнения: + # - cuboid: заполнить весь кубоид блоками + # - mask: только определённые позиции (см. ниже) + fill-mode: cuboid + + # Для режима mask (опционально): + # mask: + # marker: pink_concrete # Блок-маркер для позиций + # positions: [] # Позиции будут заполнены при сканировании + +# ============================================ +# СБРОС ШАХТЫ +# ============================================ +reset: + # Интервал сброса + # Форматы: 30s (секунды), 5m (минуты), 2h (часы), 1d (дни) + interval: "10m" + + # Процентный триггер (сброс при % заполненности) + # -1 или 0 = отключено + percent-trigger: -1.0 + percent-enabled: false + + # Сообщение при сбросе (пусто = стандартное) + # Поддерживает плейсхолдеры: {mine}, {time} + broadcast: "�FF00Шахта &#FFFF00{mine} �FF00сброшена!" + + # Команды при сбросе (опционально) + commands: [] + # - "eco give 100" + # - "broadcast Шахта {mine} сброшена!" + +# ============================================ +# ТЕЛЕПОРТАЦИЯ ПРИ СБРОСЕ +# ============================================ +teleport: + enabled: false + location: "" # Формат: мир;x;y;z;yaw;pitch + + # Точка спавна для "застрявших" игроков + # (если игрок оказался внутри блока после сброса) + spawn: + enabled: false + location: "" # Если не задано - используется teleport.location + +# ============================================ +# НАГРАДЫ ПРИ ДОБЫЧЕ +# ============================================ +# Выдаются с определённым шансом при ломании блоков + +rewards: [] + # Пример награды: + # - chance: 0.001 # 0.1% шанс + # prevent-drops: false # Отменить дроп блока? + # blocks: # На каких блоках работает + # - diamond_ore + # - emerald_ore + # items: # Предметы (опционально) + # - type: diamond + # amount: 1 + # name: "�FFFFРедкий алмаз" + # commands: # Команды (опционально) + # - "eco give 1000" + +# ============================================ +# ИНТЕРФЕЙС (UI) +# ============================================ +ui: + # ActionBar (полоска над хотбаром) + actionbar: + enabled: true + # Дистанция показа (в блоках) + range: 50.0 + # Формат строки + # Плейсхолдеры: {mine}, {percent}, {time}, {blocks}, {total} + format: "&#FFD700{mine} &#AAAAAA| �FF00{percent}% &#AAAAAA| &#FFFF00{time}" + + # Голограмма над шахтой (требует HolographicDisplays или DecentHolograms) + hologram: + enabled: false + # Высота голограммы от центра региона + height: 3.0 + format: + - "&#FFD700★ {mine} ★" + - "&#AAAAAAЗаполнено: �FF00{percent}%" + - "&#AAAAAAСброс через: &#FFFF00{time}" + + # Формат времени для плейсхолдеров + # 1 = HH:MM:SS (01:25:35) + # 2 = короткий (25m 35s) + # 3 = текстовый (1ч 25м 35с) + timer-format: 2 + +# ============================================ +# WORLDGUARD ИНТЕГРАЦИЯ (опционально) +# ============================================ +worldguard: + enabled: false + # Шаблон названия региона + # {mine_name} = название шахты + # {random_4} = 4 случайных символа + region-template: "mine_{mine_name}_{random_4}" + + # Владельцы региона (uuid:xxx или имя) + owners: [] + + # Участники региона + members: [] + + # Флаги WorldGuard + flags: + - "passthrough deny" + - "build -g members allow" + - "pvp deny" + +# ============================================ +# ДОПОЛНИТЕЛЬНЫЕ НАСТРОЙКИ +# ============================================ + +# Задержка между установкой блоков (в тиках, 20 тиков = 1 сек) +# 0 = мгновенно (может лагать на больших шахтах) +# Больше = плавнее, но медленнее +block-place-delay: 0 + +# Тип установки блоков: +# - fast: быстрый (рекомендуется) +# - bukkit: стандартный (медленнее, но стабильнее) +# - parallel: параллельный (экспериментально) +setter: fast + +# Разрешить физику блоков (песок, гравий падают)? +block-physics: false + +# Разрешить спавн сущностей в шахте? +entity-spawning: false + +# Разрешить течение жидкостей? +liquid-flow: false diff --git a/src/main/java/dev/loki/lomines/data/config/loader/region/RegionConfigLoader.java b/src/main/java/dev/loki/lomines/data/config/loader/region/RegionConfigLoader.java index 409407e..1ecf0a7 100644 --- a/src/main/java/dev/loki/lomines/data/config/loader/region/RegionConfigLoader.java +++ b/src/main/java/dev/loki/lomines/data/config/loader/region/RegionConfigLoader.java @@ -17,32 +17,17 @@ public final class RegionConfigLoader { /** * Parses region configuration from YAML. + * Supports up to 100 selection points (50 regions). */ public RegionConfig parse(YamlConfiguration yaml) { List selections = new ArrayList<>(); - // Try new format first - for (int i = 1; i <= 10; i++) { - String key = "region.selection." + i; - if (yaml.contains(key)) { - String locStr = yaml.getString(key); - if (locStr != null && !locStr.isBlank()) { - selections.add(parseLocation(locStr)); - } - } - } + // Try new format first (region.selection.X) + selections.addAll(parseSelections(yaml, "region.selection.", 100)); - // Legacy fallback + // Legacy fallback (selection.X) if (selections.isEmpty()) { - for (int i = 1; i <= 10; i++) { - String key = "selection." + i; - if (yaml.contains(key)) { - String locStr = yaml.getString(key); - if (locStr != null && !locStr.isBlank()) { - selections.add(parseLocation(locStr)); - } - } - } + selections.addAll(parseSelections(yaml, "selection.", 100)); } if (selections.size() < 2) { @@ -52,6 +37,43 @@ public final class RegionConfigLoader { return RegionConfig.fromSelections(selections); } + /** + * Parses selection points from YAML with given prefix. + * Supports indexed format: prefix.1, prefix.2, etc. + */ + private List parseSelections(YamlConfiguration yaml, String prefix, int maxPoints) { + List selections = new ArrayList<>(); + + for (int i = 1; i <= maxPoints; i++) { + String key = prefix + i; + if (yaml.contains(key)) { + String locStr = yaml.getString(key); + if (locStr != null && !locStr.isBlank()) { + try { + selections.add(parseLocation(locStr)); + } catch (IllegalArgumentException e) { + // Skip invalid locations but continue parsing + } + } + } else { + // Stop at first missing index (assuming sequential) + // But check at least a few more for gaps + if (i > 20) { + boolean hasMore = false; + for (int j = i + 1; j <= Math.min(i + 5, maxPoints); j++) { + if (yaml.contains(prefix + j)) { + hasMore = true; + break; + } + } + if (!hasMore) break; + } + } + } + + return selections; + } + /** * Saves region configuration to YAML. */ diff --git a/src/main/java/dev/loki/lomines/data/config/ui/UIConfig.java b/src/main/java/dev/loki/lomines/data/config/ui/UIConfig.java index c3eb373..6ce6bc9 100644 --- a/src/main/java/dev/loki/lomines/data/config/ui/UIConfig.java +++ b/src/main/java/dev/loki/lomines/data/config/ui/UIConfig.java @@ -1,6 +1,6 @@ package dev.loki.lomines.data.config.ui; -import net.kyori.adventure.text.minimessage.MiniMessage; +import dev.loki.lomines.util.format.ColorUtils; import net.kyori.adventure.text.Component; import java.util.Objects; @@ -36,6 +36,7 @@ public record UIConfig( /** * Parses the action bar format with placeholders. * Placeholders: {mine}, {percent}, {time}, {blocks}, {total} + * Supports colors: &#RRGGBB, &a..&f, */ public Component formatActionBar(String mineName, double percent, String timeStr, int blocks, int total) { String parsed = actionBarFormat @@ -45,7 +46,7 @@ public record UIConfig( .replace("{blocks}", String.valueOf(blocks)) .replace("{total}", String.valueOf(total)); - return MiniMessage.miniMessage().deserialize(parsed); + return ColorUtils.format(parsed); } /** diff --git a/src/main/java/dev/loki/lomines/integration/hologram/HologramManager.java b/src/main/java/dev/loki/lomines/integration/hologram/HologramManager.java index ed7ea71..922c8f0 100644 --- a/src/main/java/dev/loki/lomines/integration/hologram/HologramManager.java +++ b/src/main/java/dev/loki/lomines/integration/hologram/HologramManager.java @@ -5,7 +5,7 @@ import dev.loki.lomines.core.mine.Mine; import dev.loki.lomines.data.config.ui.HologramConfig; import dev.loki.lomines.integration.hologram.provider.DecentHologramsProvider; import dev.loki.lomines.integration.hologram.provider.HolographicDisplaysProvider; -import net.kyori.adventure.text.minimessage.MiniMessage; +import dev.loki.lomines.util.format.ColorUtils; import org.bukkit.Location; import java.util.*; @@ -144,7 +144,6 @@ public final class HologramManager { private List formatLines(List format, Mine mine) { List result = new ArrayList<>(); - MiniMessage miniMessage = MiniMessage.miniMessage(); for (String line : format) { String formatted = line @@ -153,7 +152,8 @@ public final class HologramManager { .replace("{bar}", createProgressBar(mine.getPercentFilled())) .replace("{time}", formatResetTime(mine)); - result.add(formatted); + // Convert &#RRGGBB and &codes to legacy for hologram plugins + result.add(ColorUtils.toLegacy(formatted)); } return result; @@ -163,12 +163,12 @@ public final class HologramManager { int filled = (int) Math.round(percent / 10.0); int empty = 10 - filled; + // Use &#RRGGBB format which works with most hologram plugins StringBuilder bar = new StringBuilder(); - bar.append(""); + bar.append("�FF00"); // Green for filled bar.append("█".repeat(Math.max(0, filled))); - bar.append(""); + bar.append("󅒐"); // Gray for empty bar.append("░".repeat(Math.max(0, empty))); - bar.append(""); return bar.toString(); } diff --git a/src/main/java/dev/loki/lomines/util/format/ColorUtils.java b/src/main/java/dev/loki/lomines/util/format/ColorUtils.java new file mode 100644 index 0000000..ef7eb57 --- /dev/null +++ b/src/main/java/dev/loki/lomines/util/format/ColorUtils.java @@ -0,0 +1,207 @@ +package dev.loki.lomines.util.format; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Utility for converting color formats. + * Supports: &#RRGGBB (HEX), &x&R&R&G&G&B&B (Bukkit HEX), MiniMessage + */ +public final class ColorUtils { + + private static final Pattern HEX_PATTERN = Pattern.compile("&#([0-9A-Fa-f]{6})"); + private static final Pattern LEGACY_HEX_PATTERN = Pattern.compile("&x([&0-9A-Fa-f]){12}"); + + private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage(); + private static final LegacyComponentSerializer LEGACY_SERIALIZER = LegacyComponentSerializer.builder() + .hexColors() + .useUnusualXRepeatedCharacterHexFormat() + .build(); + + private ColorUtils() { + // Utility class + } + + /** + * Converts a string with color codes to a Component. + * Supports: + * - &#RRGGBB - HEX colors + * - &x&R&R&G&G&B&B - Legacy Bukkit HEX + * - &a, &b, ... - Legacy colors + * - , <#RRGGBB> - MiniMessage (pass-through) + */ + public static Component format(String text) { + if (text == null || text.isEmpty()) { + return Component.empty(); + } + + // Check if it's already MiniMessage format + if (text.contains("<") && text.contains(">")) { + try { + return MINI_MESSAGE.deserialize(text); + } catch (Exception e) { + // Fall through to legacy conversion + } + } + + // Convert &#RRGGBB to MiniMessage <#RRGGBB> + String miniMessageText = convertHexToMiniMessage(text); + + // Convert legacy &x format if present + miniMessageText = convertLegacyHexToMiniMessage(miniMessageText); + + // Convert remaining legacy codes (&a, &l, etc.) to MiniMessage + miniMessageText = convertLegacyToMiniMessage(miniMessageText); + + try { + return MINI_MESSAGE.deserialize(miniMessageText); + } catch (Exception e) { + // Fallback: use legacy serializer + return LEGACY_SERIALIZER.deserialize(text); + } + } + + /** + * Converts &#RRGGBB to <#RRGGBB> for MiniMessage. + */ + private static String convertHexToMiniMessage(String text) { + Matcher matcher = HEX_PATTERN.matcher(text); + StringBuffer result = new StringBuffer(); + + while (matcher.find()) { + String hex = matcher.group(1); + matcher.appendReplacement(result, "<#" + hex + ">"); + } + matcher.appendTail(result); + + return result.toString(); + } + + /** + * Converts &x&R&R&G&G&B&B to <#RRGGBB> for MiniMessage. + */ + private static String convertLegacyHexToMiniMessage(String text) { + if (!text.contains("&x")) { + return text; + } + + // Pattern for &x&R&R&G&G&B&B + Pattern bukkitHex = Pattern.compile("&x(&[0-9A-Fa-f])(&[0-9A-Fa-f])(&[0-9A-Fa-f])(&[0-9A-Fa-f])(&[0-9A-Fa-f])(&[0-9A-Fa-f])"); + Matcher matcher = bukkitHex.matcher(text); + StringBuffer result = new StringBuffer(); + + while (matcher.find()) { + String r1 = matcher.group(1).substring(1); + String r2 = matcher.group(2).substring(1); + String g1 = matcher.group(3).substring(1); + String g2 = matcher.group(4).substring(1); + String b1 = matcher.group(5).substring(1); + String b2 = matcher.group(6).substring(1); + String hex = r1 + r2 + g1 + g2 + b1 + b2; + matcher.appendReplacement(result, "<#" + hex + ">"); + } + matcher.appendTail(result); + + return result.toString(); + } + + /** + * Converts legacy & codes to MiniMessage tags. + */ + private static String convertLegacyToMiniMessage(String text) { + return text + .replace("&0", "") + .replace("&1", "") + .replace("&2", "") + .replace("&3", "") + .replace("&4", "") + .replace("&5", "") + .replace("&6", "") + .replace("&7", "") + .replace("&8", "") + .replace("&9", "") + .replace("&a", "") + .replace("&b", "") + .replace("&c", "") + .replace("&d", "") + .replace("&e", "") + .replace("&f", "") + .replace("&k", "") + .replace("&l", "") + .replace("&m", "") + .replace("&n", "") + .replace("&o", "") + .replace("&r", ""); + } + + /** + * Strips all color codes from text. + */ + public static String stripColors(String text) { + if (text == null || text.isEmpty()) { + return ""; + } + return text.replaceAll("&#[0-9A-Fa-f]{6}", "") + .replaceAll("&[0-9A-Fa-fk-orK-OR]", "") + .replaceAll("<[^>]+>", ""); + } + + /** + * Converts to legacy color format (&a, &c, &x&R&R&G&G&B&B). + * Useful for plugins that don't support MiniMessage. + */ + public static String toLegacy(String text) { + if (text == null || text.isEmpty()) { + return ""; + } + + String result = text; + + // Convert MiniMessage <#RRGGBB> to &x&R&R&G&G&B&B + Pattern hexTag = Pattern.compile("<#([0-9A-Fa-f]{6})>"); + Matcher hexMatcher = hexTag.matcher(result); + StringBuffer sb = new StringBuffer(); + while (hexMatcher.find()) { + String hex = hexMatcher.group(1); + StringBuilder legacy = new StringBuilder("&x"); + for (char c : hex.toCharArray()) { + legacy.append("&").append(c); + } + hexMatcher.appendReplacement(sb, legacy.toString()); + } + hexMatcher.appendTail(sb); + result = sb.toString(); + + // Convert MiniMessage named colors to legacy + result = result + .replace("", "&0") + .replace("", "&1") + .replace("", "&2") + .replace("", "&3") + .replace("", "&4") + .replace("", "&5") + .replace("", "&6") + .replace("", "&7") + .replace("", "&8") + .replace("", "&9") + .replace("", "&a") + .replace("", "&b") + .replace("", "&c") + .replace("", "&d") + .replace("", "&e") + .replace("", "&f") + .replace("", "&k") + .replace("", "&l") + .replace("", "&m") + .replace("", "&n") + .replace("", "&o") + .replace("", "&r"); + + // &#RRGGBB is already in a good format for most plugins + return result; + } +} diff --git a/todo.md b/todo.md index 07c9fd7..d4d564e 100644 --- a/todo.md +++ b/todo.md @@ -454,6 +454,11 @@ dev.loki.lomines/ - `/lm regions ` — список регионов - `/lm addregion ` — добавить регион - `/lm removeregion ` — удалить регион +- [x] Улучшить формат конфигурации ✅ + - Пример конфига с комментариями (mines/example_mine.yml) + - Поддержка &#RRGGBB цветов + - До 100 точек селекшна (50 регионов) + - Чистый читаемый YAML формат ---