/** * BetonQuest - advanced quests for Bukkit * Copyright (C) 2016 Jakub "Co0sh" Sapalski * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package pl.betoncraft.betonquest.config; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bukkit.Sound; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; import pl.betoncraft.betonquest.BetonQuest; import pl.betoncraft.betonquest.InstructionParseException; import pl.betoncraft.betonquest.config.ConfigAccessor.AccessorType; import pl.betoncraft.betonquest.database.PlayerData; import pl.betoncraft.betonquest.utils.Debug; import pl.betoncraft.betonquest.utils.PlayerConverter; /** * Handles the configuration of the plugin * * @author Jakub Sapalski */ public class Config { private static BetonQuest plugin; private static Config instance; private static ConfigAccessor messages; private static ConfigAccessor internal; private static HashMap<String, ConfigPackage> packages = new HashMap<>(); private static HashMap<String, QuestCanceler> cancelers = new HashMap<>(); private static String lang; private static ArrayList<String> languages = new ArrayList<>(); private final static List<String> utilDirNames = Arrays.asList(new String[]{"logs", "backups", "conversations"}); private File root; public Config() { this(true); } /** * Creates new instance of the Config handler * * @param verboose * controls if this object should log it's actions to the file */ public Config(boolean verboose) { packages.clear(); cancelers.clear(); languages.clear(); instance = this; plugin = BetonQuest.getInstance(); root = plugin.getDataFolder(); lang = plugin.getConfig().getString("language"); // save default config plugin.saveDefaultConfig(); // need to be sure everything is saved plugin.reloadConfig(); plugin.saveConfig(); // load messages messages = new ConfigAccessor(new File(root, "messages.yml"), "messages.yml", AccessorType.OTHER); messages.saveDefaultConfig(); internal = new ConfigAccessor(null, "internal-messages.yml", AccessorType.OTHER); for (String key : messages.getConfig().getKeys(false)) { if (!key.equals("global")) { if (verboose) Debug.info("Loaded " + key + " language"); languages.add(key); } } // save example package createPackage("default"); // load packages for (File file : plugin.getDataFolder().listFiles()) { searchForPackages(file); } // load quest cancelers for (ConfigPackage pack : packages.values()) { ConfigurationSection s = pack.getMain().getConfig().getConfigurationSection("cancel"); if (s == null) continue; for (String key : s.getKeys(false)) { String name = pack.getName() + "." + key; try { cancelers.put(name, new QuestCanceler(name)); } catch (InstructionParseException e) { Debug.error("Could not load '" + name + "' quest canceler: " + e.getMessage()); } } } } private void searchForPackages(File file) { if (file.isDirectory() && !utilDirNames.contains(file.getName())) { File[] content = file.listFiles(); for (File subFile : content) { if (subFile.getName().equals("main.yml")) { // this is a package, add it and stop searching String packPath = BetonQuest.getInstance().getDataFolder() .toURI().relativize(file.toURI()) .toString().replace('/', ' ').trim().replace(' ', '-'); ConfigPackage pack = new ConfigPackage(file, packPath); if (pack.isEnabled()) { packages.put(packPath, pack); } return; } } for (File subFile : content) { searchForPackages(subFile); } } } /** * Creates package with the given name and populates it with default quest * * @param packName * name of the new package * @return true if the package was created, false if it already existed */ public static boolean createPackage(String packName) { File def = new File(instance.root, packName.replace("-", File.separator)); if (!def.exists()) { Debug.broadcast("Deploying " + packName + " package!"); def.mkdirs(); saveResource(def, "main.yml"); saveResource(def, "events.yml"); saveResource(def, "conditions.yml"); saveResource(def, "journal.yml"); saveResource(def, "items.yml"); saveResource(def, "objectives.yml"); File conversations = new File(def, "conversations"); conversations.mkdir(); saveResource(conversations, "defaultConversation.yml", "innkeeper.yml"); List<String> list = plugin.getConfig().getStringList("packages"); if (list == null) list = new ArrayList<>(); list.add(packName); plugin.getConfig().set("packages", list); plugin.saveConfig(); return true; } return false; } /** * Saves resource in a root directory * * @param root * directory where the resource will be saved * @param resource * resource name, also name of the file */ private static void saveResource(File root, String resource) { saveResource(root, resource, resource); } /** * Saves the resource with the name in a root directory * * @param root * directory where the resource will be saved * @param resource * resource name * @param name * file name */ private static void saveResource(File root, String resource, String name) { if (!root.isDirectory()) return; File file = new File(root, name); if (!file.exists()) { try { file.createNewFile(); InputStream in = plugin.getResource(resource); OutputStream out = new FileOutputStream(file); byte[] buffer = new byte[1024]; int len = in.read(buffer); while (len != -1) { out.write(buffer, 0, len); len = in.read(buffer); } out.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * @return the current instance of the Config handler */ public static Config getInstance() { return instance; } /** * Returns a map containing all quest cancelers from across all packages. * * @return the map with quest cancelers */ public static HashMap<String, QuestCanceler> getCancelers() { return cancelers; } /** * Retrieves the message from the configuration in specified language and * replaces the variables * * @param lang * language in which the message should be retrieved * @param message * name of the message to retrieve * @param variables * array of variables to replace * @return message in that language, or message in English, or null if it * does not exist */ public static String getMessage(String lang, String message, String[] variables) { String result = messages.getConfig().getString(lang + "." + message); if (result == null) { result = messages.getConfig().getString(Config.getLanguage() + "." + message); } if (result == null) { result = messages.getConfig().getString("en." + message); } if (result == null) { result = internal.getConfig().getString(lang + "." + message); } if (result == null) { result = internal.getConfig().getString("en." + message); } if (result != null) { if (variables != null) for (int i = 0; i < variables.length; i++) { result = result.replace("{" + (i + 1) + "}", variables[i]); } result = result.replace('&', 'ยง'); } return result; } /** * Retrieves the message from the configuration in specified language * * @param message * name of the message to retrieve * @param lang * language in which the message should be retrieved * @return message in that language, or message in English, or null if it * does not exist */ public static String getMessage(String lang, String message) { return getMessage(lang, message, null); } /** * @return the map of packages and their names */ public static Map<String, ConfigPackage> getPackages() { return packages; } /** * Retrieves the string from across all configuration. The variables are not * replaced! To replace variables automatically just call getString() method * on ConfigPackage. * * @param address * address of the string * @return the requested string */ public static String getString(String address) { if (address == null) return null; String[] parts = address.split("\\."); if (parts.length < 2) return null; String main = parts[0]; if (main.equals("config")) { return plugin.getConfig().getString(address.substring(7)); } else if (main.equals("messages")) { return messages.getConfig().getString(address.substring(9)); } else { ConfigPackage pack = packages.get(main); if (pack == null) return null; return pack.getRawString(address.substring(main.length() + 1)); } } /** * Sets the string at specified address * * @param address * address of the variable * @param value * value that needs to be set * @return true if it was set, false otherwise */ public static boolean setString(String address, String value) { if (address == null) return false; ; String[] parts = address.split("\\."); if (parts.length < 2) return false; String main = parts[0]; if (main.equals("config")) { plugin.getConfig().set(address.substring(7), value); plugin.saveConfig(); return true; } else if (main.equals("messages")) { messages.getConfig().set(address.substring(9), value); messages.saveConfig(); return true; } else { ConfigPackage pack = packages.get(main); if (pack == null) return false; return pack.setString(address.substring(main.length() + 1), value); } } /** * @return messages configuration */ public static ConfigAccessor getMessages() { return messages; } /** * @return the default language */ public static String getLanguage() { return lang; } /** * Returns the ID of a conversation assigned to specified NPC, across all * packages. If there are multiple assignments for the same value, the first * one will be returned. * * @param value * the name of the NPC (as defined in <i>main.yml</i>) * @return the ID of the conversation assigned to this NPC or null if there * isn't one */ public static String getNpc(String value) { // load npc assignments from all packages for (String packName : packages.keySet()) { ConfigPackage pack = packages.get(packName); ConfigurationSection assignemnts = pack.getMain().getConfig().getConfigurationSection("npcs"); for (String assignment : assignemnts.getKeys(false)) { if (assignment.equalsIgnoreCase(value)) { return packName + "." + assignemnts.getString(assignment); } } } return null; } /** * Sends a message to player in his chosen language or default or English * (if previous not found). * * @param playerID * ID of the player * @param messageName * ID of the message */ public static void sendMessage(String playerID, String messageName) { sendMessage(playerID, messageName, null, null, null, null); } /** * Sends a message to player in his chosen language or default or English * (if previous not found). It will replace all {x} sequences with the * variables. * * @param playerID * ID of the player * @param messageName * ID of the message * @param variables * array of variables which will be inserted into the string */ public static void sendMessage(String playerID, String messageName, String[] variables) { sendMessage(playerID, messageName, variables, null, null, null); } /** * Sends a message to player in his chosen language or default or English * (if previous not found). It will replace all {x} sequences with the * variables and play the sound. * * @param playerID * ID of the player * @param messageName * ID of the message * @param variables * array of variables which will be inserted into the string * @param soundName * name of the sound to play to the player */ public static void sendMessage(String playerID, String messageName, String[] variables, String soundName) { sendMessage(playerID, messageName, variables, soundName, null, null); } /** * Sends a message to player in his chosen language or default or English * (if previous not found). It will replace all {x} sequences with the * variables and play the sound. It will also add a prefix to the message. * * @param playerID * ID of the player * @param messageName * ID of the message * @param variables * array of variables which will be inserted into the message * @param soundName * name of the sound to play to the player * @param prefixName * ID of the prefix * @param prefixVariables * array of variables which will be inserted into the prefix */ public static void sendMessage(String playerID, String messageName, String[] variables, String soundName, String prefixName, String[] prefixVariables) { Player player = PlayerConverter.getPlayer(playerID); PlayerData playerData = BetonQuest.getInstance().getPlayerData(playerID); if (player == null || playerData == null) return; String language = playerData.getLanguage(); String message = getMessage(language, messageName, variables); if (message == null || message.length() == 0) return; if (prefixName != null) { String prefix = getMessage(language, prefixName, prefixVariables); if (prefix.length() > 0) { message = prefix + message; } } player.sendMessage(message); if (soundName != null) { String rawSound = BetonQuest.getInstance().getConfig().getString("sounds." + soundName); if (!rawSound.equalsIgnoreCase("false")) { try { player.playSound(player.getLocation(), Sound.valueOf(rawSound), 1F, 1F); } catch (IllegalArgumentException e) { Debug.error("Unknown sound type: " + rawSound); } } } } /** * @return the languages defined for this plugin */ public static ArrayList<String> getLanguages() { return languages; } public static String getDefaultPackage() { return plugin.getConfig().getString("default_package"); } }