From fddfa606b58f11eb2186a2611b168eaa084c2eb4 Mon Sep 17 00:00:00 2001 From: Andres Date: Wed, 25 Mar 2026 20:18:45 +0100 Subject: [PATCH] feat(tags): add multi-file and subdirectory support - Add recursive subdirectory support for loading tag YAML files (getAllYamlFiles) - Fix NullPointerException when rarity is not specified (defaults to common) - Refactor tag loading to support multiple YAML files in tags/ folder - Track which file each tag was loaded from for correct save/delete - Tags can now be organized in subfolders (tags/countries/, tags/flags/) --- .../supremetags/managers/ConfigManager.java | 501 +++++--- .../supremetags/managers/TagManager.java | 1127 +++++++++-------- 2 files changed, 934 insertions(+), 694 deletions(-) diff --git a/src/main/java/net/noscape/project/supremetags/managers/ConfigManager.java b/src/main/java/net/noscape/project/supremetags/managers/ConfigManager.java index 1c5bd3b..d77a17a 100644 --- a/src/main/java/net/noscape/project/supremetags/managers/ConfigManager.java +++ b/src/main/java/net/noscape/project/supremetags/managers/ConfigManager.java @@ -1,165 +1,336 @@ -package net.noscape.project.supremetags.managers; - -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.plugin.java.JavaPlugin; - -import java.io.*; -import java.util.HashMap; - -public class ConfigManager { - - private final JavaPlugin plugin; - private HashMap configs = new HashMap<>(); - - public ConfigManager(JavaPlugin plugin) { - this.plugin = plugin; - - // Load each config, ensuring defaults are only copied if the file does not exist - loadConfig("rarities.yml"); - loadConfig("tags.yml"); - loadConfig("messages.yml"); - loadConfig("banned-words.yml"); - loadConfig("categories.yml"); - loadConfig("data.yml"); - loadConfig("guis.yml"); - } - - /** - * Get the config by the name (Don't forget the .yml) - * - * @param name the name of the config file - * @return the Config object - */ - public Config getConfig(String name) { - return configs.computeIfAbsent(name, Config::new); - } - - /** - * Save the config by the name (Don't forget the .yml) - * - * @param name the name of the config file - */ - public void saveConfig(String name) { - getConfig(name).save(); - } - - /** - * Load the config, ensuring defaults are copied if the file does not exist - * - * @param name the name of the config file - */ - private void loadConfig(String name) { - Config config = getConfig(name); - config.saveDefaultConfig(); // Only saves the default config if the file does not exist - config.reload(); // Reload to ensure the config is properly loaded - } - - /** - * Reload the config by the name (Don't forget the .yml) - * - * @param name the name of the config file - */ - public void reloadConfig(String name) { - Config config = configs.get(name); - if (config == null) { - System.err.println("Config not found: " + name); - return; - } - config.reload(); - } - - public class Config { - - private final String name; - private File file; - private YamlConfiguration config; - - public Config(String name) { - this.name = name; - } - - /** - * Saves the config to file - * - * @return this Config object - */ - public Config save() { - if (config == null || file == null) { - return this; - } - try { - config.save(file); - } catch (IOException ex) { - ex.printStackTrace(); - } - return this; - } - - /** - * Gets the YamlConfiguration instance of this config, loading from file if necessary - * - * @return YamlConfiguration instance - */ - public YamlConfiguration get() { - if (config == null) { - reload(); - } - return config; - } - - /** - * Saves the default config if it doesn't exist - * - * @return this Config object - */ - public Config saveDefaultConfig() { - this.file = new File(plugin.getDataFolder(), this.name); - if (!file.exists()) { - plugin.saveResource(this.name, false); - } - return this; - } - - /** - * Reloads the config from file - */ - public void reload() { - this.file = new File(plugin.getDataFolder(), this.name); - this.config = YamlConfiguration.loadConfiguration(file); - - // Load defaults from resources if the config file doesn't exist - try (Reader defConfigStream = new InputStreamReader(plugin.getResource(name), "UTF8")) { - if (defConfigStream != null) { - YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream); - config.setDefaults(defConfig); - config.options().copyDefaults(false); // Do not overwrite existing values - } - } catch (IOException | NullPointerException e) { - e.printStackTrace(); - } - } - - /** - * An easy way to set a value into the config - * - * @param key the key - * @param value the value - * @return this Config object - */ - public Config set(String key, Object value) { - get().set(key, value); - save(); // Save changes immediately - return this; - } - - /** - * An easy way to get a value from the config - * - * @param key the key - * @return the value associated with the key - */ - public Object get(String key) { - return get().get(key); - } - } -} +package net.noscape.project.supremetags.managers; + +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class ConfigManager { + + private final JavaPlugin plugin; + private HashMap configs = new HashMap<>(); + + // --- Multi-file tag support --- + private final List tagConfigs = new ArrayList<>(); + private final List tagFiles = new ArrayList<>(); + private File tagsFolder; + private File customTagsFile; + private YamlConfiguration customTagsConfig; + + public ConfigManager(JavaPlugin plugin) { + this.plugin = plugin; + + // Load each config, ensuring defaults are only copied if the file does not exist + loadConfig("rarities.yml"); + loadConfig("messages.yml"); + loadConfig("banned-words.yml"); + loadConfig("categories.yml"); + loadConfig("data.yml"); + loadConfig("guis.yml"); + + // Load the tags/ folder (replaces the old single tags.yml) + loadTagsFolder(); + } + + // ----------------------------------------------------------------------- + // Tags folder management + // ----------------------------------------------------------------------- + + /** + * Initialises the tags/ folder, migrates any legacy tags.yml, and loads + * all .yml files found inside the folder. + */ + public void loadTagsFolder() { + tagsFolder = new File(plugin.getDataFolder(), "tags"); + if (!tagsFolder.exists()) { + tagsFolder.mkdirs(); + } + + // --- Migration: move old tags.yml into tags/default.yml --- + File legacyTagsFile = new File(plugin.getDataFolder(), "tags.yml"); + File defaultTagsFile = new File(tagsFolder, "default.yml"); + + if (legacyTagsFile.exists() && !defaultTagsFile.exists()) { + boolean moved = legacyTagsFile.renameTo(defaultTagsFile); + if (moved) { + plugin.getLogger().info("[SupremeTags] Migrated tags.yml -> tags/default.yml"); + } else { + plugin.getLogger().warning("[SupremeTags] Could not migrate tags.yml to tags/default.yml. Please move it manually."); + } + } + + // --- If the folder is still empty, copy the bundled default --- + File[] existingFiles = tagsFolder.listFiles((dir, name) -> name.endsWith(".yml")); + if (existingFiles == null || existingFiles.length == 0) { + // Save the default resource (tags/default.yml inside the JAR) + try { + plugin.saveResource("tags/default.yml", false); + } catch (IllegalArgumentException e) { + plugin.getLogger().warning("[SupremeTags] Could not save default tags/default.yml: " + e.getMessage()); + } + } + + // --- Prepare the custom-tags.yml file used for runtime-created tags --- + customTagsFile = new File(tagsFolder, "custom-tags.yml"); + if (!customTagsFile.exists()) { + try { + customTagsFile.createNewFile(); + } catch (IOException e) { + plugin.getLogger().severe("[SupremeTags] Could not create tags/custom-tags.yml: " + e.getMessage()); + } + } + + // Load all yml files in the folder + reloadTagConfigs(); + } + + /** + * Rescans the tags/ folder and reloads every .yml file in it, + * including subdirectories. + */ + public void reloadTagConfigs() { + tagConfigs.clear(); + tagFiles.clear(); + + List ymlFiles = getAllYamlFiles(tagsFolder); + + for (File file : ymlFiles) { + YamlConfiguration cfg = YamlConfiguration.loadConfiguration(file); + tagConfigs.add(cfg); + tagFiles.add(file); + } + + // Refresh the custom-tags reference + customTagsConfig = getOrCreateCustomTagsConfig(); + } + + /** + * Recursively finds all .yml files in a directory and its subdirectories. + */ + private List getAllYamlFiles(File folder) { + List ymlFiles = new ArrayList<>(); + if (folder == null || !folder.exists()) return ymlFiles; + + File[] files = folder.listFiles(); + if (files == null) return ymlFiles; + + for (File file : files) { + if (file.isDirectory()) { + // Recursively search subdirectories + ymlFiles.addAll(getAllYamlFiles(file)); + } else if (file.getName().endsWith(".yml")) { + ymlFiles.add(file); + } + } + + return ymlFiles; + } + + /** + * Returns all FileConfiguration objects loaded from the tags/ folder. + */ + public List getTagConfigs() { + return new ArrayList<>(tagConfigs); + } + + /** + * Returns the matching File for a given YamlConfiguration loaded from the + * tags/ folder, or null if not found. + */ + public File getFileForTagConfig(YamlConfiguration cfg) { + int idx = tagConfigs.indexOf(cfg); + return (idx >= 0 && idx < tagFiles.size()) ? tagFiles.get(idx) : null; + } + + /** + * Saves a specific tag config file to its original location. + */ + public void saveTagConfig(FileConfiguration cfg) { + YamlConfiguration yamlCfg = (YamlConfiguration) cfg; + File file = getFileForTagConfig(yamlCfg); + if (file == null) { + // Fallback to custom-tags.yml if not found + file = customTagsFile; + } + try { + yamlCfg.save(file); + } catch (IOException e) { + plugin.getLogger().severe("[SupremeTags] Could not save tag config: " + e.getMessage()); + } + } + + /** + * Gets or creates the custom-tags.yml configuration. + */ + private YamlConfiguration getOrCreateCustomTagsConfig() { + if (customTagsConfig == null) { + customTagsConfig = YamlConfiguration.loadConfiguration(customTagsFile); + } + return customTagsConfig; + } + + /** + * Returns the config for writing new tags (custom-tags.yml). + */ + public YamlConfiguration getTagConfigForWrite() { + return getOrCreateCustomTagsConfig(); + } + + /** + * Saves the custom-tags.yml file. + */ + public void saveCustomTagsConfig() { + try { + customTagsConfig.save(customTagsFile); + } catch (IOException e) { + plugin.getLogger().severe("[SupremeTags] Could not save custom-tags.yml: " + e.getMessage()); + } + } + + // ----------------------------------------------------------------------- + // Standard config management + // ----------------------------------------------------------------------- + + /** + * Get the config by the name (Don't forget the .yml) + * + * @param name the name of the config file + * @return the Config object + */ + public Config getConfig(String name) { + return configs.computeIfAbsent(name, Config::new); + } + + /** + * Save the config by the name (Don't forget the .yml) + * + * @param name the name of the config file + */ + public void saveConfig(String name) { + getConfig(name).save(); + } + + /** + * Load the config, ensuring defaults are copied if the file does not exist + * + * @param name the name of the config file + */ + private void loadConfig(String name) { + Config config = getConfig(name); + config.saveDefaultConfig(); // Only saves the default config if the file does not exist + config.reload(); // Reload to ensure the config is properly loaded + } + + /** + * Reload the config by the name (Don't forget the .yml) + * + * @param name the name of the config file + */ + public void reloadConfig(String name) { + Config config = configs.get(name); + if (config == null) { + System.err.println("Config not found: " + name); + return; + } + config.reload(); + } + + public class Config { + + private final String name; + private File file; + private YamlConfiguration config; + + public Config(String name) { + this.name = name; + } + + /** + * Saves the config to file + * + * @return this Config object + */ + public Config save() { + if (config == null || file == null) { + return this; + } + try { + config.save(file); + } catch (IOException ex) { + ex.printStackTrace(); + } + return this; + } + + /** + * Gets the YamlConfiguration instance of this config, loading from file if necessary + * + * @return YamlConfiguration instance + */ + public YamlConfiguration get() { + if (config == null) { + reload(); + } + return config; + } + + /** + * Saves the default config if it doesn't exist + * + * @return this Config object + */ + public Config saveDefaultConfig() { + this.file = new File(plugin.getDataFolder(), this.name); + if (!file.exists()) { + plugin.saveResource(this.name, false); + } + return this; + } + + /** + * Reloads the config from file + */ + public void reload() { + this.file = new File(plugin.getDataFolder(), this.name); + this.config = YamlConfiguration.loadConfiguration(file); + + // Load defaults from resources if the config file doesn't exist + try (Reader defConfigStream = new InputStreamReader(plugin.getResource(name), "UTF8")) { + if (defConfigStream != null) { + YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(defConfigStream); + config.setDefaults(defConfig); + config.options().copyDefaults(false); // Do not overwrite existing values + } + } catch (IOException | NullPointerException e) { + e.printStackTrace(); + } + } + + /** + * An easy way to set a value into the config + * + * @param key the key + * @param value the value + * @return this Config object + */ + public Config set(String key, Object value) { + get().set(key, value); + save(); // Save changes immediately + return this; + } + + /** + * An easy way to get a value from the config + * + * @param key the key + * @return the value associated with the key + */ + public Object get(String key) { + return get().get(key); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/noscape/project/supremetags/managers/TagManager.java b/src/main/java/net/noscape/project/supremetags/managers/TagManager.java index e3e688d..c9fa8ca 100644 --- a/src/main/java/net/noscape/project/supremetags/managers/TagManager.java +++ b/src/main/java/net/noscape/project/supremetags/managers/TagManager.java @@ -1,530 +1,599 @@ -package net.noscape.project.supremetags.managers; - -import net.noscape.project.supremetags.*; -import net.noscape.project.supremetags.handlers.Tag; -import net.noscape.project.supremetags.handlers.TagEconomy; -import net.noscape.project.supremetags.handlers.Variant; -import net.noscape.project.supremetags.storage.TagData; -import net.noscape.project.supremetags.storage.UserData; -import org.bukkit.*; -import org.bukkit.command.*; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.entity.*; -import org.bukkit.potion.PotionEffectType; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.regex.Pattern; - -import static net.noscape.project.supremetags.utils.Utils.*; - -public class TagManager { - - private Map tags = new HashMap<>(); - private final Map dataItem = new HashMap<>(); - public static final Map tagUnlockCounts = new ConcurrentHashMap<>(); - - private final FileConfiguration messages = SupremeTags.getInstance().getConfigManager().getConfig("messages.yml").get(); - private final String invalidtag = msg("messages.invalid-tag"); - private final String validtag = msg("messages.valid-tag"); - private final String invalidcategory = msg("messages.invalid-category"); - - public TagManager() {} - - /* ---------------------- CREATE TAGS ---------------------- */ - - public void createTag(CommandSender sender, String identifier, String tagText, List description, String permission, double cost) { - createTagInternal(identifier, "NAME_TAG", tagText, description, permission, cost, 0, sender); - } - - public void createTag(String identifier, String tagText, List description, String permission, double cost) { - createTagInternal(identifier, "NAME_TAG", tagText, description, permission, cost, 0, null); - } - - public void createTag(String identifier, String material, String tagText, List description, String permission, double cost) { - createTagInternal(identifier, material, tagText, description, permission, cost, 0, null); - } - - public void createTag(String identifier, String material, String tagText, List description, String permission, double cost, int modelData) { - createTagInternal(identifier, material, tagText, description, permission, cost, modelData, null); - } - - private void createTagInternal(String identifier, String material, String tagText, List description, String permission, double cost, int modelData, CommandSender sender) { - if (tags.containsKey(identifier)) { - if (sender != null) msgPlayer(sender, validtag); - return; - } - - String defaultCategory = SupremeTags.getInstance().getConfig().getString("settings.default-category"); - int orderID = tags.size() + 1; - - TagEconomy economy = new TagEconomy("VAULT", cost, false); - Tag tag = new Tag(identifier, Collections.singletonList(tagText), defaultCategory, permission, description, orderID, true, "common", new HashMap<>(), economy); - tags.put(identifier, tag); - - if (!isDBTags()) { - saveTagToConfig(tag, material, modelData, tagText); - saveTagConfig(); - } else { - TagData.createTag(tag); - } - - if (sender != null) { - msgPlayer(sender, "&8[&6&lTAG&8] &7New tag created &6" + identifier + " &f- " + tagText); - } - - unloadTags(); - loadTags(true); - } - - private void saveTagToConfig(Tag tag, String material, int modelData, String tagText) { - String id = tag.getIdentifier(); - - List voucherLore = Arrays.asList( - "&7&m-----------------------------", - "&eClick to equip!", - "&7&m-----------------------------" - ); - - getTagConfig().set("tags." + id + ".tag", tag.getTag()); - getTagConfig().set("tags." + id + ".permission", tag.getPermission()); - getTagConfig().set("tags." + id + ".description", tag.getDescription()); - getTagConfig().set("tags." + id + ".category", tag.getCategory()); - getTagConfig().set("tags." + id + ".order", tag.getOrder()); - getTagConfig().set("tags." + id + ".withdrawable", tag.isWithdrawable()); - getTagConfig().set("tags." + id + ".displayname", "&7Tag: %tag%"); - getTagConfig().set("tags." + id + ".custom-model-data", modelData); - getTagConfig().set("tags." + id + ".display-item", material); - getTagConfig().set("tags." + id + ".voucher-item.material", "NAME_TAG"); - getTagConfig().set("tags." + id + ".voucher-item.displayname", tagText + " &f&lVoucher"); - getTagConfig().set("tags." + id + ".voucher-item.custom-model-data", 0); - getTagConfig().set("tags." + id + ".voucher-item.glow", true); - getTagConfig().set("tags." + id + ".voucher-item.lore", voucherLore); - getTagConfig().set("tags." + id + ".rarity", "common"); - getTagConfig().set("tags." + id + ".economy.enabled", tag.getEconomy().isEnabled()); - getTagConfig().set("tags." + id + ".economy.type", tag.getEconomy().getType()); - getTagConfig().set("tags." + id + ".economy.amount", tag.getEconomy().getAmount()); - } - - /* ---------------------- DELETE TAGS ---------------------- */ - - public void deleteTag(CommandSender sender, String identifier) { - if (!tags.containsKey(identifier)) { - msgPlayer(sender, invalidtag); - return; - } - - tags.remove(identifier); - - if (isDBTags()) { - TagData.deleteTag(identifier); - } else { - getTagConfig().set("tags." + identifier, null); - saveTagConfig(); - reloadTagConfig(); - } - String deleted = messages.getString("messages.editor.deleted").replace("%prefix%", Objects.requireNonNull(messages.getString("messages.prefix"))); - msgPlayer(sender, deleted); - } - - /* ---------------------- LOAD & VALIDATE ---------------------- */ - - public void loadTags(boolean silent) { - if (isDBTags()) { - tags.clear(); - TagData.getAllTags(); - if (!silent) Bukkit.getConsoleSender().sendMessage("[TAGS] Loaded " + tags.size() + " tag(s) from database."); - return; - } - - FileConfiguration tagConfig = getTagConfig(); - ConfigurationSection tagsSection = tagConfig.getConfigurationSection("tags"); - - if (tagsSection == null) { - if (!silent) Bukkit.getConsoleSender().sendMessage("[TAGS] No tags found in configuration."); - return; - } - - Map loadedTags = new LinkedHashMap<>(); - int count = 0; - - for (String identifier : tagsSection.getKeys(false)) { - ConfigurationSection section = tagsSection.getConfigurationSection(identifier); - if (section == null) continue; - - List tag = normalizeList(tagConfig, "tags." + identifier + ".tag"); - List description = normalizeList(tagConfig, "tags." + identifier + ".description"); - String category = section.getString("category"); - - Map effects = parseEffects(tagConfig.getStringList("tags." + identifier + ".effects")); - List variants = new ArrayList<>(); - String rarity = section.getString("rarity"); - - ConfigurationSection variantSection = section.getConfigurationSection("variants"); - if (variantSection != null) { - for (String var : variantSection.getKeys(false)) { - if (variantSection.getBoolean(var + ".enable") || variantSection.getBoolean(var + ".enabled")) { - String permission = variantSection.getString(var + ".permission"); - List variantTag = tagConfig.getStringList("tags." + identifier + ".variants." + var + ".tag"); - List variantDescription = tagConfig.getStringList("tags." + identifier + ".variants." + var + ".description"); - if (variantDescription.isEmpty() || !tagConfig.isSet("tags." + identifier + ".variants." + var + ".description")) { - variantDescription = description; - } - - String unlocked_material = tagConfig.getString("tags." + identifier + ".variants." + var + ".item.unlocked.material", "NAME_TAG"); - int unlocked_custom_model_data = tagConfig.getInt("tags." + identifier + ".variants." + var + ".item.unlocked.custom-model-data", 0); - String unlocked_displayname = tagConfig.getString("tags." + identifier + ".variants." + var + ".item.unlocked.displayname", "&7Variant: %tag%"); - - String locked_material = tagConfig.getString("tags." + identifier + ".variants." + var + ".item.locked.material", "NAME_TAG"); - int locked_custom_model_data = tagConfig.getInt("tags." + identifier + ".variants." + var + ".item.locked.custom-model-data", 0); - String locked_displayname = tagConfig.getString("tags." + identifier + ".variants." + var + ".item.locked.displayname", "&7Variant: %tag%"); - - String rarityVariant = tagConfig.getString("tags." + identifier + ".variants." + var + ".rarity", rarity); - - Variant v = new Variant(var, identifier, variantTag, permission, variantDescription, rarityVariant); - v.setUnlocked_material(unlocked_material); - v.setUnlocked_custom_model_data(unlocked_custom_model_data); - v.setUnlocked_displayname(unlocked_displayname); - - v.setLocked_material(locked_material); - v.setLocked_custom_model_data(locked_custom_model_data); - v.setLocked_displayname(locked_displayname); - - variants.add(v); - } - } - } - - String permission = tagConfig.getString("tags." + identifier + ".permission", "none"); - int orderID = tagConfig.getInt("tags." + identifier + ".order"); - boolean withdrawable = tagConfig.getBoolean("tags." + identifier + ".withdrawable"); - - String ecoType = tagConfig.getString("tags." + identifier + ".economy.type"); - double ecoAmount = tagConfig.getInt("tags." + identifier + ".economy.amount"); - boolean ecoEnabled = false; - if (tagConfig.isSet("tags." + identifier + ".economy.enable")) { - ecoEnabled = tagConfig.getBoolean("tags." + identifier + ".economy.enable"); - } else if (tagConfig.isSet("tags." + identifier + ".economy.enabled")) { - ecoEnabled = tagConfig.getBoolean("tags." + identifier + ".economy.enabled"); - } - - String take_cmd = tagConfig.getString("tags." + identifier + ".economy.take-cmd"); - String condition = tagConfig.getString("tags." + identifier + ".economy.condition"); - - List abilities = tagConfig.getStringList("tags." + identifier + ".abilities"); - - TagEconomy economy = new TagEconomy(ecoType, ecoAmount, ecoEnabled); - if (ecoType != null && ecoType.equalsIgnoreCase("CUSTOM")) { - economy.setTake_cmd(take_cmd); - economy.setCondition(condition); - } - - Tag t = new Tag(identifier, tag, category, permission, description, orderID, withdrawable, rarity, effects, economy, variants); - - t.setEcoEnabled(ecoEnabled); - t.setEcoType(ecoType); - t.setEcoAmount(ecoAmount); - - t.setVariants(variants); - t.setAbilities(abilities); - - loadedTags.put(identifier, t); - count++; - } - - tags.clear(); - tags.putAll(loadedTags); - - for (Tag tag : tags.values()) { - if (tag.getTag().size() > 1) tag.startAnimation(); - } - - for (Variant v : getVariants()) { - if (v.getTag().size() > 1) v.startAnimation(); - } - - if (!silent) Bukkit.getConsoleSender().sendMessage("[TAGS] Loaded " + count + " tag(s) successfully."); - } - - public void validateTags(boolean from_tags_list) { - if (from_tags_list) { - for (Tag tag : tags.values()) { - String basePath = "tags." + tag.getIdentifier(); - - // Tag check - if (!getTagConfig().isSet(basePath + ".tag")) { - getTagConfig().set(basePath + ".tag", tag.getTag()); - } - - if (!getTagConfig().isSet(basePath + ".custom-placeholders")) { - getTagConfig().set(basePath + ".custom-placeholders.nopermission", "&cYou do not have any permission to use " + tag.getTag()); - getTagConfig().set(basePath + ".custom-placeholders.wheretofind", "&eYou find this tag in &b&lDiamond Crate&e!"); - } - - // Permission check - String permission = tag.getPermission() != null ? tag.getPermission() : "supremetags.tag." + tag.getIdentifier(); - if (!getTagConfig().isSet(basePath + ".permission")) { - getTagConfig().set(basePath + ".permission", permission); - } - - // Custom Model Data check - if (!getTagConfig().isSet(basePath + ".custom-model-data")) { - getTagConfig().set(basePath + ".custom-model-data", 0); - } - - // Description check - if (!getTagConfig().isSet(basePath + ".description")) { - getTagConfig().set(basePath + ".description", tag.getDescription()); - } - - // Category check - String category = tag.getCategory() != null ? tag.getCategory() : SupremeTags.getInstance().getConfig().getString("settings.default-category"); - if (!getTagConfig().isSet(basePath + ".category")) { - getTagConfig().set(basePath + ".category", category); - } - - // Order check - if (!getTagConfig().isSet(basePath + ".order")) { - getTagConfig().set(basePath + ".order", tag.getOrder()); - } - - // Withdrawable check - if (!getTagConfig().isSet(basePath + ".withdrawable")) { - getTagConfig().set(basePath + ".withdrawable", tag.isWithdrawable()); - } - - // Economy check - if (!getTagConfig().isSet(basePath + ".economy")) { - getTagConfig().set(basePath + ".economy.enabled", tag.getEconomy().isEnabled()); - getTagConfig().set(basePath + ".economy.type", tag.getEconomy().getType()); - getTagConfig().set(basePath + ".economy.amount", tag.getEconomy().getAmount()); - } - - if (!getTagConfig().isSet(basePath + ".rarity")) { - getTagConfig().set(basePath + ".rarity", "common"); - } - } - - saveTagConfig(); - } else { - for (String identifier : getTagConfig().getConfigurationSection("tags").getKeys(false)) { - String basePath = "tags." + identifier; - - // Tag check - if (!getTagConfig().isSet(basePath + ".tag")) { - getTagConfig().set(basePath + ".tag", "&8[&e&l" + identifier.toUpperCase() + "&8]"); - } - - // Permission check - if (!getTagConfig().isSet(basePath + ".permission")) { - getTagConfig().set(basePath + ".permission", "supremetags.tag." + identifier); - } - - // Custom Model Data check - if (!getTagConfig().isSet(basePath + ".custom-model-data")) { - getTagConfig().set(basePath + ".custom-model-data", 0); - } - - // Description check - List description = new ArrayList<>(); - description.add(identifier + " Tag!"); - if (!getTagConfig().isSet(basePath + ".description")) { - getTagConfig().set(basePath + ".description", description); - } - - // Category check - if (!getTagConfig().isSet(basePath + ".category")) { - getTagConfig().set(basePath + ".category", SupremeTags.getInstance().getConfig().getString("settings.default-category")); - } - - // Withdrawable check - if (!getTagConfig().isSet(basePath + ".withdrawable")) { - getTagConfig().set(basePath + ".withdrawable", true); - } - - if (!getTagConfig().isSet(basePath + ".economy")) { - getTagConfig().set(basePath + ".economy.enabled", false); - getTagConfig().set(basePath + ".economy.type", "VAULT"); - getTagConfig().set(basePath + ".economy.amount", 200); - } - - if (!getTagConfig().isSet(basePath + ".rarity")) { - getTagConfig().set(basePath + ".rarity", "common"); - } - } - - saveTagConfig(); - } - } - - /* ---------------------- GETTERS & UTIL ---------------------- */ - - public Variant getVariant(String variantIdentifier) { - for (Tag tag : getTags().values()) { - for (Variant var : tag.getVariants()) { - if (var.getIdentifier().equalsIgnoreCase(variantIdentifier)) return var; - } - } - return null; - } - - public List getVariants() { - List variants = new ArrayList<>(); - for (Tag tag : getTags().values()) variants.addAll(tag.getVariants()); - return variants; - } - - public boolean isVariant(String variantIdentifier) { - return getVariant(variantIdentifier) != null; - } - - public boolean hasVariantTag(OfflinePlayer player) { - return getVariant(UserData.getActive(player.getUniqueId())) != null; - } - - public Variant getVariantTag(OfflinePlayer player) { - return hasVariantTag(player) ? getVariant(UserData.getActive(player.getUniqueId())) : null; - } - - public Tag getTag(String identifier) { - return tags.get(identifier); - } - - public boolean doesTagExist(String identifier) { - return getTag(identifier) != null; - } - - public void unloadTags() { - tags.clear(); - } - - public Map getTags() { - return tags; - } - - public Map getDataItem() { - return dataItem; - } - - public void saveTag(Tag tag) { - if (isDBTags()) { - TagData.updateTag(tag); - } else { - String identifier = tag.getIdentifier(); - getTagConfig().set("tags." + identifier + ".tag", tag.getTag()); - getTagConfig().set("tags." + identifier + ".permission", tag.getPermission()); - getTagConfig().set("tags." + identifier + ".description", tag.getDescription()); - getTagConfig().set("tags." + identifier + ".category", tag.getCategory()); - getTagConfig().set("tags." + identifier + ".economy.amount", tag.getEconomy().getAmount()); - getTagConfig().set("tags." + identifier + ".withdrawable", tag.isWithdrawable()); - saveTagConfig(); - } - } - - public void setTag(CommandSender sender, String identifier, String tag) { - if (tags.containsKey(identifier)) { - Tag t = tags.get(identifier); - List tagsList = t.getTag(); - tagsList.add(tag); - t.setTag(tagsList); - - try { - getTagConfig().set("tags." + identifier + ".tag", tagsList); - saveTagConfig(); - reloadTagConfig(); - } catch (Exception e) { - e.printStackTrace(); - } - - msgPlayer(sender, "&8[&6&lTAG&8] &6" + t.getIdentifier() + "'s tag &7changed to " + t.getCurrentTag()); - } else { - msgPlayer(sender, invalidtag); - } - } - - public void setCategory(CommandSender sender, String identifier, String category) { - if (SupremeTags.getInstance().getTagManager().getTag(identifier) == null) { - msgPlayer(sender, invalidtag); - return; - } - - if (!SupremeTags.getInstance().getCategoryManager().getCatorgies().contains(category)) { - msgPlayer(sender, invalidcategory); - return; - } - - Tag t = tags.get(identifier); - t.setCategory(category); - - try { - SupremeTags.getInstance().getTagManager().getTagConfig().set("tags." + identifier + ".category", t.getCategory()); - SupremeTags.getInstance().getTagManager().saveTagConfig(); - } catch (Exception e) { - e.printStackTrace(); - } - - msgPlayer(sender, "&8[&6&lTAG&8] &6" + t.getIdentifier() + "'s category &7changed to " + t.getCategory()); - } - - public static Map parseEffects(List effectList) { - Map effectsMap = new HashMap<>(); - for (String entry : effectList) { - String[] parts = entry.split(":"); - if (parts.length != 2) continue; - PotionEffectType type = PotionEffectType.getByName(parts[0].toUpperCase()); - if (type == null) continue; - try { - int level = Integer.parseInt(parts[1]); - effectsMap.put(type, level); - } catch (NumberFormatException ignored) { - } - } - return effectsMap; - } - - private List normalizeList(FileConfiguration config, String path) { - Object val = config.get(path); - if (val instanceof String) return Collections.singletonList((String) val); - if (val instanceof List) return config.getStringList(path); - return new ArrayList<>(); - } - - private String msg(String path) { - return Objects.requireNonNull(messages.getString(path)) - .replaceAll("%prefix%", Objects.requireNonNull(messages.getString("messages.prefix"))); - } - - public void saveTagConfig() { - SupremeTags.getInstance().getConfigManager().saveConfig("tags.yml"); - } - - public FileConfiguration getTagConfig() { - return SupremeTags.getInstance().getConfigManager().getConfig("tags.yml").get(); - } - - public void reloadTagConfig() { - SupremeTags.getInstance().getConfigManager().reloadConfig("tags.yml"); - } - - public boolean tagExists(String name) { - return getTag(name) != null; - } - - public boolean tagExistsNearName(String name) { - Pattern pattern = Pattern.compile(Pattern.quote(name), Pattern.CASE_INSENSITIVE); - return tags.values().stream().map(Tag::getIdentifier).anyMatch(id -> pattern.matcher(id).find()); - } - - public FileConfiguration getMessages() { - return messages; - } - - public boolean isDBTags() { - return SupremeTags.getInstance().isDBTags(); - } - - public void setTagsMap(Map tags) { - this.tags = tags; - } +package net.noscape.project.supremetags.managers; + +import net.noscape.project.supremetags.*; +import net.noscape.project.supremetags.handlers.Tag; +import net.noscape.project.supremetags.handlers.TagEconomy; +import net.noscape.project.supremetags.handlers.Variant; +import net.noscape.project.supremetags.storage.TagData; +import net.noscape.project.supremetags.storage.UserData; +import org.bukkit.*; +import org.bukkit.command.*; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.*; +import org.bukkit.potion.PotionEffectType; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Pattern; + +import static net.noscape.project.supremetags.utils.Utils.*; + +public class TagManager { + + private Map tags = new HashMap<>(); + private final Map dataItem = new HashMap<>(); + public static final Map tagUnlockCounts = new ConcurrentHashMap<>(); + + /** + * Tracks which FileConfiguration each tag was loaded from. + * Used so that saves and deletes go back to the correct file. + */ + private final Map tagSourceConfig = new HashMap<>(); + + private final FileConfiguration messages = SupremeTags.getInstance().getConfigManager().getConfig("messages.yml").get(); + private final String invalidtag = msg("messages.invalid-tag"); + private final String validtag = msg("messages.valid-tag"); + private final String invalidcategory = msg("messages.invalid-category"); + + public TagManager() {} + + /* ---------------------- CREATE TAGS ---------------------- */ + + public void createTag(CommandSender sender, String identifier, String tagText, List description, String permission, double cost) { + createTagInternal(identifier, "NAME_TAG", tagText, description, permission, cost, 0, sender); + } + + public void createTag(String identifier, String tagText, List description, String permission, double cost) { + createTagInternal(identifier, "NAME_TAG", tagText, description, permission, cost, 0, null); + } + + public void createTag(String identifier, String material, String tagText, List description, String permission, double cost) { + createTagInternal(identifier, material, tagText, description, permission, cost, 0, null); + } + + public void createTag(String identifier, String material, String tagText, List description, String permission, double cost, int modelData) { + createTagInternal(identifier, material, tagText, description, permission, cost, modelData, null); + } + + private void createTagInternal(String identifier, String material, String tagText, List description, String permission, double cost, int modelData, CommandSender sender) { + if (tags.containsKey(identifier)) { + if (sender != null) msgPlayer(sender, validtag); + return; + } + + String defaultCategory = SupremeTags.getInstance().getConfig().getString("settings.default-category"); + int orderID = tags.size() + 1; + + TagEconomy economy = new TagEconomy("VAULT", cost, false); + Tag tag = new Tag(identifier, Collections.singletonList(tagText), defaultCategory, permission, description, orderID, true, "common", new HashMap<>(), economy); + tags.put(identifier, tag); + + if (!isDBTags()) { + // New tags always go to the custom-tags.yml write target + FileConfiguration writeConfig = getTagConfigForWrite(); + saveTagToConfig(writeConfig, tag, material, modelData, tagText); + saveSpecificTagConfig(writeConfig); + tagSourceConfig.put(identifier, writeConfig); + } else { + TagData.createTag(tag); + } + + if (sender != null) { + msgPlayer(sender, "\u00268[\u00266\u0026lTAG\u00268] \u00267New tag created \u00266" + identifier + " \u0026f- " + tagText); + } + + unloadTags(); + loadTags(true); + } + + private void saveTagToConfig(FileConfiguration config, Tag tag, String material, int modelData, String tagText) { + String id = tag.getIdentifier(); + + List voucherLore = Arrays.asList( + "\u00267\u0026m-----------------------------", + "\u0026eClick to equip!", + "\u00267\u0026m-----------------------------" + ); + + config.set("tags." + id + ".tag", tag.getTag()); + config.set("tags." + id + ".permission", tag.getPermission()); + config.set("tags." + id + ".description", tag.getDescription()); + config.set("tags." + id + ".category", tag.getCategory()); + config.set("tags." + id + ".order", tag.getOrder()); + config.set("tags." + id + ".withdrawable", tag.isWithdrawable()); + config.set("tags." + id + ".displayname", "\u00267Tag: %tag%"); + config.set("tags." + id + ".custom-model-data", modelData); + config.set("tags." + id + ".display-item", material); + config.set("tags." + id + ".voucher-item.material", "NAME_TAG"); + config.set("tags." + id + ".voucher-item.displayname", tagText + " \u0026f\u0026lVoucher"); + config.set("tags." + id + ".voucher-item.custom-model-data", 0); + config.set("tags." + id + ".voucher-item.glow", true); + config.set("tags." + id + ".voucher-item.lore", voucherLore); + config.set("tags." + id + ".rarity", "common"); + config.set("tags." + id + ".economy.enabled", tag.getEconomy().isEnabled()); + config.set("tags." + id + ".economy.type", tag.getEconomy().getType()); + config.set("tags." + id + ".economy.amount", tag.getEconomy().getAmount()); + } + + /* ---------------------- DELETE TAGS ---------------------- */ + + public void deleteTag(CommandSender sender, String identifier) { + if (!tags.containsKey(identifier)) { + msgPlayer(sender, invalidtag); + return; + } + + tags.remove(identifier); + + if (isDBTags()) { + TagData.deleteTag(identifier); + } else { + // Find which config holds this tag and remove it from there + FileConfiguration sourceConfig = tagSourceConfig.remove(identifier); + if (sourceConfig != null) { + sourceConfig.set("tags." + identifier, null); + saveSpecificTagConfig(sourceConfig); + reloadTagConfig(); + } else { + // Fallback: search all configs + for (FileConfiguration cfg : SupremeTags.getInstance().getConfigManager().getTagConfigs()) { + if (cfg.isConfigurationSection("tags." + identifier)) { + cfg.set("tags." + identifier, null); + saveSpecificTagConfig(cfg); + reloadTagConfig(); + break; + } + } + } + } + String deleted = messages.getString("messages.editor.deleted").replace("%prefix%", Objects.requireNonNull(messages.getString("messages.prefix"))); + msgPlayer(sender, deleted); + } + + /* ---------------------- LOAD & VALIDATE ---------------------- */ + + public void loadTags(boolean silent) { + if (isDBTags()) { + tags.clear(); + tagSourceConfig.clear(); + TagData.getAllTags(); + if (!silent) Bukkit.getConsoleSender().sendMessage("[TAGS] Loaded " + tags.size() + " tag(s) from database."); + return; + } + + Map loadedTags = new LinkedHashMap<>(); + tagSourceConfig.clear(); + int count = 0; + + // Iterate over ALL tag config files + List allTagConfigs = SupremeTags.getInstance().getConfigManager().getTagConfigs(); + + for (FileConfiguration tagConfig : allTagConfigs) { + ConfigurationSection tagsSection = tagConfig.getConfigurationSection("tags"); + if (tagsSection == null) continue; + + for (String identifier : tagsSection.getKeys(false)) { + // If a tag with this identifier was already loaded from a previous file, skip it + if (loadedTags.containsKey(identifier)) { + Bukkit.getConsoleSender().sendMessage("[TAGS] Warning: duplicate tag identifier '" + identifier + "' found in a secondary file - skipping."); + continue; + } + + ConfigurationSection section = tagsSection.getConfigurationSection(identifier); + if (section == null) continue; + + List tag = normalizeList(tagConfig, "tags." + identifier + ".tag"); + List description = normalizeList(tagConfig, "tags." + identifier + ".description"); + String category = section.getString("category"); + + Map effects = parseEffects(tagConfig.getStringList("tags." + identifier + ".effects")); + List variants = new ArrayList<>(); + String rarity = section.getString("rarity", "common"); + + ConfigurationSection variantSection = section.getConfigurationSection("variants"); + if (variantSection != null) { + for (String var : variantSection.getKeys(false)) { + if (variantSection.getBoolean(var + ".enable") || variantSection.getBoolean(var + ".enabled")) { + String permission = variantSection.getString(var + ".permission"); + List variantTag = tagConfig.getStringList("tags." + identifier + ".variants." + var + ".tag"); + List variantDescription = tagConfig.getStringList("tags." + identifier + ".variants." + var + ".description"); + if (variantDescription.isEmpty() || !tagConfig.isSet("tags." + identifier + ".variants." + var + ".description")) { + variantDescription = description; + } + + String unlocked_material = tagConfig.getString("tags." + identifier + ".variants." + var + ".item.unlocked.material", "NAME_TAG"); + int unlocked_custom_model_data = tagConfig.getInt("tags." + identifier + ".variants." + var + ".item.unlocked.custom-model-data", 0); + String unlocked_displayname = tagConfig.getString("tags." + identifier + ".variants." + var + ".item.unlocked.displayname", "\u00267Variant: %tag%"); + + String locked_material = tagConfig.getString("tags." + identifier + ".variants." + var + ".item.locked.material", "NAME_TAG"); + int locked_custom_model_data = tagConfig.getInt("tags." + identifier + ".variants." + var + ".item.locked.custom-model-data", 0); + String locked_displayname = tagConfig.getString("tags." + identifier + ".variants." + var + ".item.locked.displayname", "\u00267Variant: %tag%"); + + String rarityVariant = tagConfig.getString("tags." + identifier + ".variants." + var + ".rarity", rarity); + + Variant v = new Variant(var, identifier, variantTag, permission, variantDescription, rarityVariant); + v.setUnlocked_material(unlocked_material); + v.setUnlocked_custom_model_data(unlocked_custom_model_data); + v.setUnlocked_displayname(unlocked_displayname); + + v.setLocked_material(locked_material); + v.setLocked_custom_model_data(locked_custom_model_data); + v.setLocked_displayname(locked_displayname); + + variants.add(v); + } + } + } + + String permission = tagConfig.getString("tags." + identifier + ".permission", "none"); + int orderID = tagConfig.getInt("tags." + identifier + ".order"); + boolean withdrawable = tagConfig.getBoolean("tags." + identifier + ".withdrawable"); + + String ecoType = tagConfig.getString("tags." + identifier + ".economy.type"); + double ecoAmount = tagConfig.getInt("tags." + identifier + ".economy.amount"); + boolean ecoEnabled = false; + if (tagConfig.isSet("tags." + identifier + ".economy.enable")) { + ecoEnabled = tagConfig.getBoolean("tags." + identifier + ".economy.enable"); + } else if (tagConfig.isSet("tags." + identifier + ".economy.enabled")) { + ecoEnabled = tagConfig.getBoolean("tags." + identifier + ".economy.enabled"); + } + + String take_cmd = tagConfig.getString("tags." + identifier + ".economy.take-cmd"); + String condition = tagConfig.getString("tags." + identifier + ".economy.condition"); + + List abilities = tagConfig.getStringList("tags." + identifier + ".abilities"); + + TagEconomy economy = new TagEconomy(ecoType, ecoAmount, ecoEnabled); + if (ecoType != null && ecoType.equalsIgnoreCase("CUSTOM")) { + economy.setTake_cmd(take_cmd); + economy.setCondition(condition); + } + + Tag t = new Tag(identifier, tag, category, permission, description, orderID, withdrawable, rarity, effects, economy, variants); + + t.setEcoEnabled(ecoEnabled); + t.setEcoType(ecoType); + t.setEcoAmount(ecoAmount); + + t.setVariants(variants); + t.setAbilities(abilities); + + loadedTags.put(identifier, t); + tagSourceConfig.put(identifier, tagConfig); // track the source file + count++; + } + } + + tags.clear(); + tags.putAll(loadedTags); + + for (Tag tag : tags.values()) { + if (tag.getTag().size() > 1) tag.startAnimation(); + } + + for (Variant v : getVariants()) { + if (v.getTag().size() > 1) v.startAnimation(); + } + + if (!silent) Bukkit.getConsoleSender().sendMessage("[TAGS] Loaded " + count + " tag(s) successfully from " + allTagConfigs.size() + " file(s)."); + } + + public void validateTags(boolean from_tags_list) { + if (from_tags_list) { + for (Tag tag : tags.values()) { + String basePath = "tags." + tag.getIdentifier(); + FileConfiguration cfg = getConfigForTag(tag.getIdentifier()); + + if (!cfg.isSet(basePath + ".tag")) { + cfg.set(basePath + ".tag", tag.getTag()); + } + + if (!cfg.isSet(basePath + ".custom-placeholders")) { + cfg.set(basePath + ".custom-placeholders.nopermission", "&cYou do not have any permission to use " + tag.getTag()); + cfg.set(basePath + ".custom-placeholders.wheretofind", "&eYou find this tag in &b&lDiamond Crate&e!"); + } + + String permission = tag.getPermission() != null ? tag.getPermission() : "supremetags.tag." + tag.getIdentifier(); + if (!cfg.isSet(basePath + ".permission")) { + cfg.set(basePath + ".permission", permission); + } + + if (!cfg.isSet(basePath + ".custom-model-data")) { + cfg.set(basePath + ".custom-model-data", 0); + } + + if (!cfg.isSet(basePath + ".description")) { + cfg.set(basePath + ".description", tag.getDescription()); + } + + String category = tag.getCategory() != null ? tag.getCategory() : SupremeTags.getInstance().getConfig().getString("settings.default-category"); + if (!cfg.isSet(basePath + ".category")) { + cfg.set(basePath + ".category", category); + } + + if (!cfg.isSet(basePath + ".order")) { + cfg.set(basePath + ".order", tag.getOrder()); + } + + if (!cfg.isSet(basePath + ".withdrawable")) { + cfg.set(basePath + ".withdrawable", tag.isWithdrawable()); + } + + if (!cfg.isSet(basePath + ".economy")) { + cfg.set(basePath + ".economy.enabled", tag.getEconomy().isEnabled()); + cfg.set(basePath + ".economy.type", tag.getEconomy().getType()); + cfg.set(basePath + ".economy.amount", tag.getEconomy().getAmount()); + } + + if (!cfg.isSet(basePath + ".rarity")) { + cfg.set(basePath + ".rarity", "common"); + } + } + + // Save all configs that were modified + for (FileConfiguration cfg : new HashSet<>(tagSourceConfig.values())) { + saveSpecificTagConfig(cfg); + } + } else { + for (FileConfiguration tagConfig : SupremeTags.getInstance().getConfigManager().getTagConfigs()) { + ConfigurationSection section = tagConfig.getConfigurationSection("tags"); + if (section == null) continue; + + for (String identifier : section.getKeys(false)) { + String basePath = "tags." + identifier; + + if (!tagConfig.isSet(basePath + ".tag")) { + tagConfig.set(basePath + ".tag", "\u00268[\u0026e\u0026l" + identifier.toUpperCase() + "\u00268]"); + } + if (!tagConfig.isSet(basePath + ".permission")) { + tagConfig.set(basePath + ".permission", "supremetags.tag." + identifier); + } + if (!tagConfig.isSet(basePath + ".custom-model-data")) { + tagConfig.set(basePath + ".custom-model-data", 0); + } + if (!tagConfig.isSet(basePath + ".description")) { + List description = new ArrayList<>(); + description.add(identifier + " Tag!"); + tagConfig.set(basePath + ".description", description); + } + if (!tagConfig.isSet(basePath + ".category")) { + tagConfig.set(basePath + ".category", SupremeTags.getInstance().getConfig().getString("settings.default-category")); + } + if (!tagConfig.isSet(basePath + ".withdrawable")) { + tagConfig.set(basePath + ".withdrawable", true); + } + if (!tagConfig.isSet(basePath + ".economy")) { + tagConfig.set(basePath + ".economy.enabled", false); + tagConfig.set(basePath + ".economy.type", "VAULT"); + tagConfig.set(basePath + ".economy.amount", 200); + } + if (!tagConfig.isSet(basePath + ".rarity")) { + tagConfig.set(basePath + ".rarity", "common"); + } + } + + // Save this config after validating all tags in it + saveSpecificTagConfig(tagConfig); + } + } + } + + /* ---------------------- GETTERS & UTIL ---------------------- */ + + public Variant getVariant(String variantIdentifier) { + for (Tag tag : getTags().values()) { + for (Variant var : tag.getVariants()) { + if (var.getIdentifier().equalsIgnoreCase(variantIdentifier)) return var; + } + } + return null; + } + + public List getVariants() { + List variants = new ArrayList<>(); + for (Tag tag : getTags().values()) variants.addAll(tag.getVariants()); + return variants; + } + + public boolean isVariant(String variantIdentifier) { + return getVariant(variantIdentifier) != null; + } + + public boolean hasVariantTag(OfflinePlayer player) { + return getVariant(UserData.getActive(player.getUniqueId())) != null; + } + + public Variant getVariantTag(OfflinePlayer player) { + return hasVariantTag(player) ? getVariant(UserData.getActive(player.getUniqueId())) : null; + } + + public Tag getTag(String identifier) { + return tags.get(identifier); + } + + public boolean doesTagExist(String identifier) { + return getTag(identifier) != null; + } + + public void unloadTags() { + tags.clear(); + tagSourceConfig.clear(); + } + + public Map getTags() { + return tags; + } + + public Map getDataItem() { + return dataItem; + } + + public void saveTag(Tag tag) { + if (isDBTags()) { + TagData.updateTag(tag); + } else { + String identifier = tag.getIdentifier(); + FileConfiguration cfg = getConfigForTag(identifier); + cfg.set("tags." + identifier + ".tag", tag.getTag()); + cfg.set("tags." + identifier + ".permission", tag.getPermission()); + cfg.set("tags." + identifier + ".description", tag.getDescription()); + cfg.set("tags." + identifier + ".category", tag.getCategory()); + cfg.set("tags." + identifier + ".economy.amount", tag.getEconomy().getAmount()); + cfg.set("tags." + identifier + ".withdrawable", tag.isWithdrawable()); + saveSpecificTagConfig(cfg); + } + } + + public void setTag(CommandSender sender, String identifier, String tag) { + if (tags.containsKey(identifier)) { + Tag t = tags.get(identifier); + List tagsList = t.getTag(); + tagsList.add(tag); + t.setTag(tagsList); + + try { + FileConfiguration cfg = getConfigForTag(identifier); + cfg.set("tags." + identifier + ".tag", tagsList); + saveSpecificTagConfig(cfg); + reloadTagConfig(); + } catch (Exception e) { + e.printStackTrace(); + } + + msgPlayer(sender, "\u00268[\u00266\u0026lTAG\u00268] \u00266" + t.getIdentifier() + "'s tag \u00267changed to " + t.getCurrentTag()); + } else { + msgPlayer(sender, invalidtag); + } + } + + public void setCategory(CommandSender sender, String identifier, String category) { + if (SupremeTags.getInstance().getTagManager().getTag(identifier) == null) { + msgPlayer(sender, invalidtag); + return; + } + + if (!SupremeTags.getInstance().getCategoryManager().getCatorgies().contains(category)) { + msgPlayer(sender, invalidcategory); + return; + } + + Tag t = tags.get(identifier); + t.setCategory(category); + + try { + FileConfiguration cfg = getConfigForTag(identifier); + cfg.set("tags." + identifier + ".category", t.getCategory()); + saveSpecificTagConfig(cfg); + } catch (Exception e) { + e.printStackTrace(); + } + + msgPlayer(sender, "\u00268[\u00266\u0026lTAG\u00268] \u00266" + t.getIdentifier() + "'s category \u00267changed to " + t.getCategory()); + } + + public static Map parseEffects(List effectList) { + Map effectsMap = new HashMap<>(); + for (String entry : effectList) { + String[] parts = entry.split(":"); + if (parts.length != 2) continue; + PotionEffectType type = PotionEffectType.getByName(parts[0].toUpperCase()); + if (type == null) continue; + try { + int level = Integer.parseInt(parts[1]); + effectsMap.put(type, level); + } catch (NumberFormatException ignored) { + } + } + return effectsMap; + } + + private List normalizeList(FileConfiguration config, String path) { + Object val = config.get(path); + if (val instanceof String) return Collections.singletonList((String) val); + if (val instanceof List) return config.getStringList(path); + return new ArrayList<>(); + } + + private String msg(String path) { + return Objects.requireNonNull(messages.getString(path)) + .replaceAll("%prefix%", Objects.requireNonNull(messages.getString("messages.prefix"))); + } + + // ----------------------------------------------------------------------- + // Config access helpers + // ----------------------------------------------------------------------- + + /** + * Returns the FileConfiguration that contains the given tag identifier. + * Falls back to the write config (custom-tags.yml) if not found. + */ + public FileConfiguration getConfigForTag(String identifier) { + FileConfiguration src = tagSourceConfig.get(identifier); + if (src != null) return src; + // Search through all loaded configs + for (FileConfiguration cfg : SupremeTags.getInstance().getConfigManager().getTagConfigs()) { + if (cfg.isConfigurationSection("tags." + identifier)) return cfg; + } + return getTagConfigForWrite(); + } + + /** + * Returns the write target: tags/custom-tags.yml. + * Used when creating new tags at runtime. + */ + public FileConfiguration getTagConfigForWrite() { + return SupremeTags.getInstance().getConfigManager().getTagConfigForWrite(); + } + + /** + * Legacy compatibility: returns the write target config. + * Code that uses this directly will write to custom-tags.yml. + */ + public FileConfiguration getTagConfig() { + return getTagConfigForWrite(); + } + + /** + * Saves the config file that corresponds to a given FileConfiguration. + */ + public void saveSpecificTagConfig(FileConfiguration cfg) { + SupremeTags.getInstance().getConfigManager().saveTagConfig((YamlConfiguration) cfg); + } + + /** + * Legacy compatibility shim. Saves the custom-tags.yml. + */ + public void saveTagConfig() { + SupremeTags.getInstance().getConfigManager().saveCustomTagsConfig(); + } + + /** + * Reloads ALL tag config files from the tags/ folder. + */ + public void reloadTagConfig() { + SupremeTags.getInstance().getConfigManager().reloadTagConfigs(); + } + + public boolean tagExists(String name) { + return getTag(name) != null; + } + + public boolean tagExistsNearName(String name) { + Pattern pattern = Pattern.compile(Pattern.quote(name), Pattern.CASE_INSENSITIVE); + return tags.values().stream().map(Tag::getIdentifier).anyMatch(id -> pattern.matcher(id).find()); + } + + public FileConfiguration getMessages() { + return messages; + } + + public boolean isDBTags() { + return SupremeTags.getInstance().isDBTags(); + } + + public void setTagsMap(Map tags) { + this.tags = tags; + } } \ No newline at end of file