Java tutorial
/** * 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.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.file.Files; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Item; import org.bukkit.inventory.ItemStack; import pl.betoncraft.betonquest.BetonQuest; import pl.betoncraft.betonquest.InstructionParseException; import pl.betoncraft.betonquest.config.ConfigAccessor.AccessorType; import pl.betoncraft.betonquest.database.Connector; import pl.betoncraft.betonquest.database.Connector.QueryType; import pl.betoncraft.betonquest.database.Connector.UpdateType; import pl.betoncraft.betonquest.database.Database; import pl.betoncraft.betonquest.database.Saver.Record; import pl.betoncraft.betonquest.item.QuestItem; import pl.betoncraft.betonquest.utils.Debug; import pl.betoncraft.betonquest.utils.Utils; /** * Updates configuration files to the newest version. * * @author Jakub Sapalski */ public class ConfigUpdater { // abandon all hope, ye who enter here /** * Error which should be displayed to the player when something goes wrong */ private final String ERROR = "There was an error during updating process! Please " + "downgrade to the previous working version of the plugin and restore your " + "configuration from the backup. Don't forget to send this error to the developer" + ", so he can fix it! Sorry for inconvenience, here's the link:" + " <https://github.com/Co0sh/BetonQuest/issues> and a cookie: <http://i.imgur.com/iR4UMH5.png>"; /** * BetonQuest's instance */ private BetonQuest instance = BetonQuest.getInstance(); /** * Main configuration instance */ private FileConfiguration config = instance.getConfig(); /** * Destination version. At the end of the updating process this will be the * current version */ private final String destination = "v53"; /** * Deprecated ConfigHandler, used for updating older configuration files */ private ConfigHandler ch; public ConfigUpdater() { String version = BetonQuest.getInstance().getConfig().getString("version", null); Debug.info("Initializing updater with version " + version + ", destination is " + destination); // when the config is up to date then check for pending names // conversion; // conversion will occur only if UUID is manually set to true if (config.getString("uuid") != null && config.getString("uuid").equals("true") && config.getString("convert") != null && config.getString("convert").equals("true")) { convertNamesToUUID(); config.set("convert", null); instance.saveConfig(); } // move backup files to backup folder for (File file : instance.getDataFolder().listFiles()) { if (file.getName().matches("^backup-.*\\.zip$")) { file.renameTo(new File(file.getParentFile().getAbsolutePath() + File.separator + "backups" + File.separator + file.getName())); Debug.broadcast("File " + file.getName() + " moved to backup folder!"); } } if (version != null && version.equals(destination)) { Debug.broadcast("Configuration up to date!"); return; } else { Utils.backup(); } // instantiate old configuration handler ch = new ConfigHandler(); // if the version is null the plugin is updated from pre-1.3 version // (which can be 1.0, 1.1 or 1.2) if (version == null) { updateTo1_3(); } else if (version.equals("1.3")) { updateTo1_4(); } else if (version.equals("1.4")) { updateTo1_4_1(); } else if (version.equals("1.4.1")) { updateTo1_4_2(); } else if (version.equals("1.4.2")) { updateTo1_4_3(); } else if (version.equals("1.4.3")) { updateTo1_5(); } else if (version.equals("1.5")) { updateTo1_5_1(); } else if (version.equals("1.5.1")) { updateTo1_5_2(); } else if (version.equals("1.5.2")) { updateTo1_5_3(); } else if (version.equals("1.5.3") || version.equals("1.5.4") || version.equals("1.6")) { updateTo1_6(); } else if (version.matches("^v\\d+$")) { performUpdate(); } else { Debug.broadcast("Something is not right with configuration version. Consider fixing this."); } } /** * Performs full update in new updating system. */ private void performUpdate() { // this is new, post-1.5.3 updating system, where config versions // are numbered separately from plugin's releases Debug.broadcast("Updating configuration to version " + destination); update(); updateLanguages(); instance.saveConfig(); // reload configuration file to apply all possible changes new Config(false); Debug.broadcast("Successfully updated configuration!"); addChangelog(); } /** * Invokes method that updates config from current version to the next. It * repeats itself until everything is converted. */ private void update() { String version = config.getString("version", null); // if the version is the same as destination, updating process is // finished if (version == null || version.equals(destination)) return; try { // reload existing configuration new Config(false); config = instance.getConfig(); // call the right updating method Method method = this.getClass().getDeclaredMethod("update_from_" + version); method.setAccessible(true); Debug.info("Starting update from " + version + "!"); method.invoke(this); Debug.info("Update to " + config.getString("version") + " done!"); } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { e.printStackTrace(); // return, so it does not fall into an infinite loop return; } // update again until destination is reached update(); } @SuppressWarnings("unused") private void update_from_v52() { config.set("hook.bountifulapi", "true"); Debug.broadcast("Added compatibility with BountifulAPI"); config.set("version", "v53"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v51() { config.set("hook.betonlangapi", "true"); Debug.broadcast("Added compatibility with BetonLangAPI"); config.set("version", "v52"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v50() { Debug.info("Moving custom settings from main.yml to custom.yml"); List<String> coreSettings = Arrays.asList(new String[] { "npcs", "variables", "static", "global_locations", "cancel", "journal_main_page", "compass", "enabled" }); for (ConfigPackage pack : Config.getPackages().values()) { Debug.info(" Moving custom settings in package " + pack.getName()); ConfigAccessor main = pack.getMain(); ConfigAccessor custom = pack.getCustom(); main: for (String key : main.getConfig().getKeys(false)) { for (String coreSetting : coreSettings) { if (key.equals(coreSetting)) { Debug.info(" Key " + key + " is core setting, skipping"); continue main; } } Debug.info(" Key " + key + " is custom, moving it"); custom.getConfig().set(key, main.getConfig().get(key)); main.getConfig().set(key, null); } main.saveConfig(); custom.saveConfig(); } Debug.broadcast("Moved custom settings from main.yml to custom.yml file"); config.set("version", "v51"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v49() { Set<String> enabledPackages = new HashSet<>(config.getStringList("packages")); Debug.info("Disabling packages not listed in the config"); for (Iterator<ConfigPackage> iterator = Config.getPackages().values().iterator(); iterator.hasNext();) { ConfigPackage pack = iterator.next(); Debug.info(" Looking at package " + pack.getName()); if (!enabledPackages.contains(pack.getName())) { Debug.info(" Package is not enabled, removing it from the list."); pack.getMain().getConfig().set("enabled", false); pack.getMain().saveConfig(); iterator.remove(); } else { pack.getMain().getConfig().set("enabled", true); pack.getMain().saveConfig(); } } Debug.info("All packages enabled/disabled, removing 'packages' section from config"); config.set("packages", null); Debug.broadcast("Moved package enabling from config to main files"); config.set("version", "v50"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v48() { for (ConfigPackage pack : Config.getPackages().values()) { String packName = pack.getName(); List<ConfigAccessor> sections = new ArrayList<>(); // the idea is to get index of location argument for every type // and use a method to replace last semicolon with a space, because // all range arguments are right next to location arguments sections.add(pack.getConditions()); sections.add(pack.getEvents()); sections.add(pack.getObjectives()); for (ConfigAccessor acc : sections) { AccessorType type = acc.getType(); ConfigurationSection sec = acc.getConfig(); for (String key : sec.getKeys(false)) { String value = sec.getString(key); int i = value.indexOf(' '); if (i < 0) { continue; } String object = value.substring(0, i).toLowerCase(); int index = -1; switch (type) { case CONDITIONS: switch (object) { case "location": index = 1; break; case "monsters": index = 2; break; } break; case EVENTS: switch (object) { case "clear": index = 2; break; } break; case OBJECTIVES: switch (object) { case "action": // action objective uses optional argument, so convert it manually String[] parts = value.split(" "); String loc = null; for (String part : parts) { if (part.startsWith("loc:")) { loc = part; break; } } if (loc != null) { int j = loc.lastIndexOf(';'); if (j < 0 || j >= loc.length() - 1) { continue; } String front = loc.substring(0, j); String back = loc.substring(j + 1); String newLoc = front + " range:" + back; sec.set(key, value.replace(loc, newLoc)); } break; case "arrow": index = 1; break; case "location": index = 1; break; } break; default: break; } if (index >= 0) { sec.set(key, semicolonToSpace(value, index)); } } acc.saveConfig(); } } Debug.broadcast("Converted additional location arguments to the new format"); config.set("version", "v49"); instance.saveConfig(); } private String semicolonToSpace(String string, int argument) { if (string == null) { return null; } String[] parts = string.split(" "); if (parts.length <= argument) { return null; } String original = parts[argument]; int lastSemicolon = original.lastIndexOf(';'); if (lastSemicolon < 0) { return null; } char[] chars = original.toCharArray(); chars[lastSemicolon] = ' '; String replaced = new String(chars); return string.replace(original, replaced); } @SuppressWarnings("unused") private void update_from_v47() { config.set("quest_items_unbreakable", "true"); Debug.broadcast("Added option to disable quest item unbreakability"); config.set("version", "v48"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v46() { config.set("journal.full_main_page", "false"); Debug.broadcast("Added 'full_main_page' option to config"); config.set("version", "v47"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v45() { config.set("hook.legendquest", "true"); Debug.broadcast("Added compatibility with LegendQuest"); config.set("hook.worldedit", "true"); Debug.broadcast("Added compatibility with WorldEdit"); config.set("version", "v46"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v44() { try { Debug.info("Translating items in 'potion' objectives"); for (ConfigPackage pack : Config.getPackages().values()) { String packName = pack.getName(); Debug.info(" Handling " + packName + " package"); FileConfiguration objectives = pack.getObjectives().getConfig(); FileConfiguration items = pack.getItems().getConfig(); for (String key : objectives.getKeys(false)) { String instruction = objectives.getString(key); if (!instruction.startsWith("potion ")) { continue; } Debug.info(" Found potion objective: '" + instruction + "'"); String[] parts = instruction.split(" "); if (parts.length < 2) { Debug.info(" It's incorrect."); continue; } int data; try { data = Integer.parseInt(parts[1]); } catch (NumberFormatException e) { Debug.info(" It's incorrect"); continue; } ItemStack itemStack = new QuestItem("potion data:" + data).generate(1); { // it doesn't work without actually spawning the item in-game... World world = Bukkit.getWorlds().get(0); Location loc = new Location(world, 0, 254, 0); Item item = world.dropItem(loc, itemStack); itemStack = item.getItemStack(); item.remove(); } String updatedInstruction = QuestItem.itemToString(itemStack); Debug.info(" Potion instruction: '" + updatedInstruction + "'"); String item = null; for (String itemKey : items.getKeys(false)) { if (items.getString(itemKey).equals(updatedInstruction)) { item = itemKey; } } if (item == null) { if (items.contains("potion")) { int index = 2; while (items.contains("potion" + index)) { index++; } item = "potion" + index; } else { item = "potion"; } } Debug.info(" The item with this instruction has key " + item); items.set(item, updatedInstruction); objectives.set(key, instruction.replace(String.valueOf(data), item)); } pack.getItems().saveConfig(); pack.getObjectives().saveConfig(); } } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } Debug.broadcast("Translated items in 'potion' objective"); config.set("display_chat_after_conversation", "false"); Debug.broadcast("Added an option to display chat messages after the conversation"); config.set("version", "v45"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v43() { try { Debug.info("Translating potion instructions"); for (ConfigPackage pack : Config.getPackages().values()) { String packName = pack.getName(); Debug.info(" Handling " + packName + " package"); FileConfiguration items = pack.getItems().getConfig(); for (String key : items.getKeys(false)) { String instruction = items.getString(key); if (!instruction.toLowerCase().startsWith("potion ") && !instruction.startsWith("splash_potion ")) { continue; } Debug.info(" Found " + key + " potion with instruction '" + instruction + "'"); try { QuestItem questItem = new QuestItem(instruction); ItemStack itemStack = questItem.generate(1); { // it doesn't work without actually spawning the item in-game... World world = Bukkit.getWorlds().get(0); Location loc = new Location(world, 0, 254, 0); Item item = world.dropItem(loc, itemStack); itemStack = item.getItemStack(); item.remove(); // lol } String updatedInstruction = QuestItem.itemToString(itemStack); Debug.info(" New instruction: '" + updatedInstruction + "'"); items.set(key, updatedInstruction); } catch (InstructionParseException e) { Debug.info("Item " + packName + "." + key + " was incorrect, skipping."); } } pack.getItems().saveConfig(); } } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } Debug.broadcast("Translated potions to a new format"); config.set("hook.racesandclasses", "true"); Debug.broadcast("Added compatibility with RacesAndClasses"); config.set("version", "v44"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v42() { config.set("hook.holographicdisplays", "true"); Debug.broadcast("Added compatibility with HolographicDisplays"); config.set("version", "v43"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v41() { try { // change raw material names in craft objectives to items from items.yml for (ConfigPackage pack : Config.getPackages().values()) { String packName = pack.getName(); ConfigAccessor objectives = pack.getObjectives(); ConfigAccessor items = pack.getItems(); ArrayList<String> materials = new ArrayList<>(); // get a list of materials and their data values for (String key : objectives.getConfig().getKeys(false)) { String objective = objectives.getConfig().getString(key); if (objective.startsWith("craft ")) { String[] parts = objective.split(" "); if (parts.length > 1) { materials.add(parts[1]); } } } // translate materials to item instructions ArrayList<String> itemInstructions = new ArrayList<>(); for (String material : materials) { if (material.contains(":")) { String[] parts = material.split(":"); String materialName = parts[0]; String data = parts[1]; itemInstructions.add(materialName + " data:" + data); } else { itemInstructions.add(material); } } // find items with the same instruction and store them in map (material, itemID) HashMap<String, String> itemIDs = new HashMap<>(); for (int i = 0; i < materials.size(); i++) { String material = materials.get(i); String itemInstruction = itemInstructions.get(i); String itemID = null; // look for existing items for (String key : items.getConfig().getKeys(false)) { if (items.getConfig().getString(key).equalsIgnoreCase(itemInstruction)) { itemID = key; break; } } // if there are no such items, create them if (itemID == null) { String materialName = material.contains(":") ? material.split(":")[0] : material; if (items.getConfig().contains(materialName)) { int index = 2; while (items.getConfig().contains(materialName + index)) { index++; } items.getConfig().set(materialName + index, itemInstruction); itemID = materialName + index; } else { items.getConfig().set(materialName, itemInstruction); itemID = materialName; } } itemIDs.put(material, itemID); } items.saveConfig(); // replace materials in craft objectives for (String key : objectives.getConfig().getKeys(false)) { String objective = objectives.getConfig().getString(key); if (objective.startsWith("craft ")) { String[] parts = objective.split(" "); if (parts.length > 1) { objectives.getConfig().set(key, objective.replace(parts[1], itemIDs.get(parts[1]))); } } } objectives.saveConfig(); } } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } Debug.broadcast("Changed 'craft' objective to use items.yml"); config.set("version", "v42"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v40() { config.set("hook.placeholderapi", "true"); Debug.broadcast("Added compatibility with PlaceholderAPI"); config.set("version", "v41"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v39() { config.set("hook.shopkeepers", "true"); Debug.broadcast("Added compatibility with Shopkeepers"); config.set("version", "v40"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v38() { boolean enabled = config.getString("autoupdate").equalsIgnoreCase("true"); config.set("autoupdate", null); config.set("update.enabled", enabled); config.set("update.download_bugfixes", true); config.set("update.notify_new_release", true); Debug.broadcast("Modified autoupdater"); config.set("version", "v39"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v37() { try { Debug.info("Updating global location tags in the database"); Debug.info(" oiienwfiu wenfiu nweiufn weiunf iuwenf iuw"); for (ConfigPackage pack : Config.getPackages().values()) { String packName = pack.getName(); String locList = pack.getMain().getConfig().getString("global_locations"); Debug.info(" Handling package '" + packName + "': " + locList); if (locList == null) { continue; } for (String locName : locList.split(",")) { Debug.info("Adding '" + packName + "' prefix to '" + locName + "' global location tags."); instance.getSaver().add(new Record(UpdateType.RENAME_ALL_TAGS, new String[] { packName + ".global_" + locName, "global_" + locName })); } } } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } Debug.broadcast("Updated tags of global locations with package names"); config.set("version", "v38"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v36() { config.set("hook.quests", "true"); Debug.broadcast("Added compatibility with Quests"); config.set("version", "v37"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v35() { config.set("hook.denizen", "true"); Debug.broadcast("Added compatibility with Denizen"); config.set("hook.skillapi", "true"); Debug.broadcast("Added compatibility with SkillAPI"); config.set("version", "v36"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v34() { config.set("hook.magic", "true"); Debug.broadcast("Added compatibility with Magic"); config.set("version", "v35"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v33() { config.set("hook.heroes", "true"); Debug.broadcast("Added compatibility with Heroes"); config.set("version", "v34"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v32() { config.set("hook.playerpoints", "true"); Debug.broadcast("Added compatibility with PlayerPoints"); config.set("version", "v33"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v31() { config.set("hook.effectlib", "true"); config.set("effectlib_npc_effect.class", "VortexEffect"); config.set("effectlib_npc_effect.iterations", 20); config.set("effectlib_npc_effect.particle", "crit_magic"); config.set("effectlib_npc_effect.helixes", 3); config.set("effectlib_npc_effect.circles", 1); config.set("effectlib_npc_effect.grow", 0.1); config.set("effectlib_npc_effect.radius", 0.5); config.set("effectlib_npc_effect.delay", 5); Debug.broadcast("Added compatibility with EffectLib"); config.set("version", "v32"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v30() { try { Debug.info("Converting cancelers to a new format"); for (ConfigPackage pack : Config.getPackages().values()) { String packName = pack.getName(); Debug.info("Searching " + packName + " package"); ConfigurationSection s = pack.getMain().getConfig().getConfigurationSection("cancel"); if (s == null) continue; for (String key : s.getKeys(false)) { String instruction = s.getString(key); Debug.info(" Converting " + key + " canceler: " + instruction); String[] parts = instruction.split(" "); HashMap<String, String> names = new HashMap<>(); String events = null, conditions = null, tags = null, points = null, objectives = null, journal = null, loc = null; for (String part : parts) { Debug.info(" Checking part " + part); if (part.startsWith("name:")) { Debug.info(" Found general name: " + part.substring(5)); names.put(Config.getLanguage(), part.substring(5)); } else if (part.startsWith("name_")) { int colonIndex = part.indexOf(':'); if (colonIndex < 0) continue; String lang = part.substring(5, colonIndex); Debug.info(" Found " + lang + " name: " + part.substring(colonIndex)); names.put(lang, part.substring(colonIndex)); } else if (part.startsWith("events:")) { Debug.info(" Found events: " + part.substring(7)); events = part.substring(7); } else if (part.startsWith("conditions:")) { Debug.info(" Found conditions: " + part.substring(11)); conditions = part.substring(11); } else if (part.startsWith("tags:")) { Debug.info(" Found tags: " + part.substring(5)); tags = part.substring(5); } else if (part.startsWith("points:")) { Debug.info(" Found points: " + part.substring(7)); points = part.substring(7); } else if (part.startsWith("objectives:")) { Debug.info(" Found objectives: " + part.substring(11)); objectives = part.substring(11); } else if (part.startsWith("journal:")) { Debug.info(" Found journal entries: " + part.substring(8)); journal = part.substring(8); } else if (part.startsWith("loc:")) { Debug.info(" Found location: " + part.substring(4)); loc = part.substring(4); } } Debug.info(" - Setting the values"); s.set(key, null); for (String lang : names.keySet()) { s.set(key + ".name." + lang, names.get(lang)); } s.set(key + ".events", events); s.set(key + ".conditions", conditions); s.set(key + ".tags", tags); s.set(key + ".points", points); s.set(key + ".objectives", objectives); s.set(key + ".journal", journal); s.set(key + ".loc", loc); pack.getMain().saveConfig(); } } } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } Debug.broadcast("Made quest cancelers more convenient to define"); config.set("version", "v31"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v29() { try { for (ConfigPackage pack : Config.getPackages().values()) { String packName = pack.getName(); ConfigurationSection section = pack.getMain().getConfig().getConfigurationSection("variables"); for (String key : section.getKeys(true)) { String variable = section.getString(key); if (variable.matches( "^\\$[a-zA-Z0-9]+\\$->\\(\\-?\\d+\\.?\\d*,\\-?\\d+\\.?\\d*,\\-?\\d+\\.?\\d*\\)$")) { section.set(key, variable.replace(',', ';')); } } pack.getMain().saveConfig(); } } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } Debug.broadcast("Changed commas to semicolons in vector variables"); config.set("version", "v30"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v28() { String globalName = "global"; try { HashMap<String, ArrayList<String>> tags = new HashMap<>(); HashMap<String, ArrayList<String>> points = new HashMap<>(); // this will ensure that there is no "global" package already // defined int i = 1; while (Config.getPackages().get(globalName) != null) { i++; globalName = "global-" + i; } Debug.info("Global package will be called '" + globalName + "'"); // create lists for tags/points that are duplicated across multiple // packages // these will be "global", the rest will be converted to their local // packages ArrayList<String> globalTagList = new ArrayList<>(); ArrayList<String> globalPointList = new ArrayList<>(); tags.put(globalName, globalTagList); points.put(globalName, globalPointList); ArrayList<ConfigPackage> packages = new ArrayList<>(); for (ConfigPackage pack : Config.getPackages().values()) { String packName = pack.getName(); Debug.info(" Checking '" + packName + "' package"); // skip packages that already use prefixes String prefixOption = pack.getString("main.tag_point_prefix"); if (prefixOption != null && prefixOption.equalsIgnoreCase("true")) continue; Debug.info(" - It's outdated, extracting tags and points from events"); packages.add(pack); // create array lists ArrayList<String> tagList = new ArrayList<>(); ArrayList<String> pointList = new ArrayList<>(); tags.put(packName, tagList); points.put(packName, pointList); // handle all tags/points in events for (String key : pack.getEvents().getConfig().getKeys(false)) { Debug.info(" Checking '" + key + "' event"); String rawInstruction = pack.getEvents().getConfig().getString(key); ArrayList<String> instructions = new ArrayList<>(); // run event also needs to be checked in case it contained // any tags if (rawInstruction.startsWith("run ")) { Debug.info(" - It's \"run\" event, extracting additional instructions"); // this part is copied from run event String[] parts = rawInstruction.substring(3).trim().split(" "); StringBuilder builder = new StringBuilder(); for (String part : parts) { if (part.startsWith("^")) { if (builder.length() != 0) { instructions.add(builder.toString().trim()); builder = new StringBuilder(); } builder.append(part.substring(1) + " "); } else { builder.append(part + " "); } } instructions.add(builder.toString().trim()); } else { // if it's not run event, add whole instruction string instructions.add(rawInstruction); } // check every instruction that was specified for (String instruction : instructions) { if (instruction.startsWith("tag ")) { Debug.info(" Found tag event, extracting tag"); String[] parts = instruction.split(" "); // check if it contains the tag, if not - continue if (parts.length < 3) { Debug.info(" - Could not find tags"); continue; } // add tag to the list if it does not contain a // package for (String tag : parts[2].split(",")) { if (!tag.contains(".")) tagList.add(tag); } } else if (instruction.startsWith("point ")) { Debug.info(" Found point event, extracting points"); String[] parts = instruction.split(" "); // check if the point has defined a category if (parts.length < 2) { Debug.info(" - Could not find the category"); continue; } // add point to the list if it does not contain a // package if (!parts[1].contains(".")) pointList.add(parts[1]); } } // done, all tags in events are extracted } Debug.info(" All tags and points extracted from events, moving to conditions"); // handle all tags/points in conditions for (String key : pack.getConditions().getConfig().getKeys(false)) { Debug.info(" Checking '" + key + "' condition"); String rawInstruction = pack.getConditions().getConfig().getString(key); ArrayList<String> instructions = new ArrayList<>(); // check condition also needs to be checked in case it // contained any tags if (rawInstruction.startsWith("check ")) { Debug.info(" - It's \"check\" condition, extracting additional instructions"); // this part is copied from run event String[] parts = rawInstruction.substring(5).trim().split(" "); StringBuilder builder = new StringBuilder(); for (String part : parts) { if (part.startsWith("^")) { if (builder.length() != 0) { instructions.add(builder.toString().trim()); builder = new StringBuilder(); } builder.append(part.substring(1) + " "); } else { builder.append(part + " "); } } instructions.add(builder.toString().trim()); } else { // if it's not check condition, add whole instruction // string instructions.add(rawInstruction); } // check every instruction that was specified for (String instruction : instructions) { if (instruction.startsWith("tag ")) { Debug.info(" Found tag condition, extracting tag"); String[] parts = instruction.split(" "); // check if it contains the tag, if not - continue if (parts.length < 2) { Debug.info(" - Could not find the tag"); continue; } // add tag to the list if it does not contain a // package if (!parts[1].contains(".")) tagList.add(parts[1]); } else if (instruction.startsWith("point ")) { Debug.info(" Found point condition, extracting points"); String[] parts = instruction.split(" "); // check if the point has defined a category if (parts.length < 2) { Debug.info(" - Could not find the category"); continue; } // add point to the list if it does not contain a // package if (!parts[1].contains(".")) pointList.add(parts[1]); } } // done, all tags in conditions are extracted } Debug.info(" All tags and points extracted from conditions"); // done, events and conditions in package extracted } Debug.info("All tags and points in all packages extracted, checking tags for duplicates"); // find tags/points that are duplicated in package hashMaps, // put them to global package and remove from those packages // first tags in each package for (int j = 0; j < packages.size(); j++) { Debug.info(" Checking list '" + packages.get(j).getName() + "'"); // get a list ArrayList<String> list = tags.get(packages.get(j).getName()); // and for each element for (int k = 0; k < list.size(); k++) { String checked = list.get(k); Debug.info(" Checking tag '" + checked + "'"); // go to each next package for (int l = j + 1; l < packages.size(); l++) { ArrayList<String> nextList = tags.get(packages.get(l).getName()); // and check if it contains that element if (nextList.contains(checked)) { Debug.info( " - list '" + packages.get(l).getName() + "' contains this tag, removing"); nextList.remove(checked); if (!globalTagList.contains(checked)) { globalTagList.add(checked); Debug.info(" Tag was added to the global list"); } } } } } Debug.info("List of global tags is filled, checking points"); // now points in each package for (int j = 0; j < packages.size(); j++) { Debug.info(" Checking list '" + packages.get(j).getName() + "'"); // get a list ArrayList<String> list = points.get(packages.get(j).getName()); // and for each element for (int k = 0; k < list.size(); k++) { String checked = list.get(k); Debug.info(" Checking point '" + checked + "'"); // go to each next package for (int l = j + 1; l < packages.size(); l++) { ArrayList<String> nextList = points.get(packages.get(l).getName()); // and check if it contains that element if (nextList.contains(checked)) { Debug.info( " - list '" + packages.get(l).getName() + "' contains this point, removing"); nextList.remove(checked); if (!globalPointList.contains(checked)) { globalPointList.add(checked); Debug.info(" Point was added to the global list"); } } } } } Debug.info("List of global points is filled, now adding \"global\" prefix in configuration files"); // done, global lists are filled for (ConfigPackage pack : packages) { Debug.info(" Replacing in '" + pack.getName() + "' package"); // update tags/points in events for (String key : pack.getEvents().getConfig().getKeys(false)) { Debug.info(" Replacing tags/points in '" + key + "' event"); String instruction = pack.getEvents().getConfig().getString(key); if (instruction.startsWith("tag ")) { Debug.info(" Found tag event, replacing tags"); String[] parts = instruction.split(" "); // check if it contains the tag, if not - continue if (parts.length < 3) { Debug.info(" - Could not find tags"); continue; } // replace tags String[] localTags = parts[2].split(","); for (int j = 0; j < localTags.length; j++) if (globalTagList.contains(localTags[j])) { String replaced = globalName + "." + localTags[j]; Debug.info(" Replacing '" + localTags[j] + "' with '" + replaced + "'"); localTags[j] = replaced; } pack.getEvents().getConfig().set(key, instruction.replace(parts[2], StringUtils.join(Arrays.asList(localTags), ','))); } else if (instruction.startsWith("point ")) { Debug.info(" Found point event, replacing points"); String[] parts = instruction.split(" "); // check if the point has defined a category if (parts.length < 2) { Debug.info(" - Could not find the category"); continue; } // replace points category if (globalPointList.contains(parts[1])) { String replaced = globalName + "." + parts[1]; Debug.info(" Replacing '" + parts[1] + "' with '" + replaced + "'"); pack.getEvents().getConfig().set(key, StringUtils.replaceOnce(instruction, parts[1], replaced)); } } else if (instruction.startsWith("run ")) { Debug.info(" Found run event, looking for tags and points"); String[] parts = instruction.split(" "); for (int j = 0; j < parts.length; j++) { // if the part is beginning of the "tag" instruction // and it contains a tag if (parts[j].equals("^tag") && j + 2 < parts.length) { Debug.info(" There is a tag event, replacing tags"); String[] localTags = parts[j + 2].split(","); for (int k = 0; k < localTags.length; k++) if (globalTagList.contains(localTags[k])) { String replaced = globalName + "." + localTags[k]; Debug.info( " Replacing '" + localTags[k] + "' with '" + replaced + "'"); localTags[k] = replaced; } parts[j + 2] = StringUtils.join(Arrays.asList(localTags), ','); } else if (parts[j].equals("^point") && j + 1 < parts.length) { Debug.info(" There is a point event, replacing points"); if (globalTagList.contains(parts[j + 1])) { String replaced = globalName + "." + parts[j + 1]; Debug.info(" Replacing '" + parts[j + 1] + "' with '" + replaced + "'"); parts[j + 1] = replaced; } } } pack.getEvents().getConfig().set(key, StringUtils.join(Arrays.asList(parts), ' ')); } } pack.getEvents().saveConfig(); Debug.info(" All tags/points replaced in all events"); // done, everything replaced in events // replacing tags/points in conditions for (String key : pack.getConditions().getConfig().getKeys(false)) { Debug.info(" Replacing tags/points in '" + key + "' condition"); String instruction = pack.getConditions().getConfig().getString(key); if (instruction.startsWith("tag ")) { Debug.info(" Found tag condition, replacing the tag"); String[] parts = instruction.split(" "); // check if it contains the tag, if not - continue if (parts.length < 2) { Debug.info(" - Could not find tags"); continue; } // replace tag if (globalTagList.contains(parts[1])) { String replaced = globalName + "." + parts[1]; Debug.info(" Replacing '" + parts[1] + "' with '" + replaced + "'"); pack.getConditions().getConfig().set(key, StringUtils.replaceOnce(instruction, parts[1], replaced)); } } else if (instruction.startsWith("point ")) { Debug.info(" Found point condition, replacing points"); String[] parts = instruction.split(" "); // check if the point has defined a category if (parts.length < 2) { Debug.info(" - Could not find the category"); continue; } // replace points category if (globalPointList.contains(parts[1])) { String replaced = globalName + "." + parts[1]; Debug.info(" Replacing '" + parts[1] + "' with '" + replaced + "'"); pack.getConditions().getConfig().set(key, StringUtils.replaceOnce(instruction, parts[1], replaced)); } } else if (instruction.startsWith("check ")) { Debug.info(" Found check condition, looking for tags and points"); String[] parts = instruction.split(" "); for (int j = 0; j < parts.length; j++) { // if the part is beginning of the "tag" instruction // and it contains a tag if (parts[j].equals("^tag") && j + 1 < parts.length) { Debug.info(" There is a tag condition, replacing tags"); if (globalTagList.contains(parts[j + 1])) { String replaced = globalName + "." + parts[j + 1]; Debug.info(" Replacing '" + parts[j + 1] + "' with '" + replaced + "'"); parts[j + 1] = replaced; } } else if (parts[j].equals("^point") && j + 1 < parts.length) { Debug.info(" There is a point condition, replacing points"); if (globalTagList.contains(parts[j + 1])) { String replaced = globalName + "." + parts[j + 1]; Debug.info(" Replacing '" + parts[j + 1] + "' with '" + replaced + "'"); parts[j + 1] = replaced; } } } pack.getConditions().getConfig().set(key, StringUtils.join(Arrays.asList(parts), ' ')); } } pack.getConditions().saveConfig(); Debug.info(" All tags/points replaced in all conditions, time for quest cancelers"); // done, everything replaced in conditions // time for quest cancelers for (String key : pack.getMain().getConfig().getConfigurationSection("cancel").getKeys(false)) { Debug.info(" Replacing tags/points in '" + key + "' canceler"); String instruction = pack.getMain().getConfig().getString("cancel." + key); String[] parts = instruction.split(" "); for (int j = 0; j < parts.length; j++) { if (parts[j].startsWith("tags:")) { String[] localTags = parts[j].substring(5).split(","); for (int k = 0; k < localTags.length; k++) { if (globalTagList.contains(localTags[k])) { String replaced = globalName + "." + localTags[k]; Debug.info(" Replaced tag '" + localTags[k] + "' to '" + replaced + "'"); localTags[k] = replaced; } } parts[j] = "tags:" + StringUtils.join(Arrays.asList(localTags), ','); } else if (parts[j].startsWith("points:")) { String[] localPoints = parts[j].substring(5).split(","); for (int k = 0; k < localPoints.length; k++) { if (globalPointList.contains(localPoints[k])) { String replaced = globalName + "." + localPoints[k]; Debug.info( " Replaced point '" + localPoints[k] + "' to '" + replaced + "'"); localPoints[k] = replaced; } } parts[j] = "points:" + StringUtils.join(Arrays.asList(localPoints), ','); } } pack.getMain().getConfig().set("cancel." + key, StringUtils.join(Arrays.asList(parts), " ")); } Debug.info(" All tags/points replaced in quest cancelers"); pack.getMain().saveConfig(); } // done, all packages have replaced tags and points Debug.info( "Done, all global tags and points are prefixed as global everywhere in every package. Updating the database."); for (String packName : tags.keySet()) { for (String tag : tags.get(packName)) { instance.getSaver().add( new Record(UpdateType.RENAME_ALL_TAGS, new String[] { packName + "." + tag, tag })); } } for (String packName : points.keySet()) { for (String point : points.get(packName)) { instance.getSaver().add(new Record(UpdateType.RENAME_ALL_POINTS, new String[] { packName + "." + point, point })); } } // remove "tag_point_prefix" option from main.yml files for (ConfigPackage pack : Config.getPackages().values()) { String packName = pack.getName(); ConfigAccessor main = pack.getMain(); main.getConfig().set("tag_point_prefix", null); main.saveConfig(); } Debug.info("Done, all cross-package tags and points are now global, the rest is local."); } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } Debug.broadcast("Moved all package-less cross-package tags and points to \"" + globalName + "\" package (you probably won't notice this change)"); config.set("version", "v29"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v27() { try { config.set("journal.chars_per_page", "245"); config.set("journal.one_entry_per_page", "false"); config.set("journal.reversed_order", "false"); config.set("journal.hide_date", "false"); } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } Debug.broadcast("Added journal options"); config.set("version", "v28"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v26() { try { for (ConfigPackage pack : Config.getPackages().values()) { String packName = pack.getName(); for (String convName : pack.getConversationNames()) { FileConfiguration conv = pack.getConversation(convName).getConfig(); ConfigurationSection playerSection = conv.getConfigurationSection("player_options"); if (playerSection != null) { for (String playerKey : playerSection.getKeys(false)) { if (conv.isConfigurationSection("player_options." + playerKey + ".text")) { for (String langKey : conv .getConfigurationSection("player_options." + playerKey + ".text") .getKeys(false)) { conv.set("player_options." + playerKey + ".text." + langKey, conv.getString("player_options." + playerKey + ".text." + langKey) .replace("%quester%", "%npc%")); } } else { conv.set("player_options." + playerKey + ".text", conv.getString("player_options." + playerKey + ".text").replace("%quester%", "%npc%")); } } } ConfigurationSection npcSection = conv.getConfigurationSection("NPC_options"); if (npcSection != null) { for (String npcKey : npcSection.getKeys(false)) { if (conv.isConfigurationSection("NPC_options." + npcKey + ".text")) { for (String langKey : conv .getConfigurationSection("NPC_options." + npcKey + ".text") .getKeys(false)) { conv.set("NPC_options." + npcKey + ".text." + langKey, conv.getString("NPC_options." + npcKey + ".text." + langKey) .replace("%quester%", "%npc%")); } } else { conv.set("NPC_options." + npcKey + ".text", conv.getString("NPC_options." + npcKey + ".text").replace("%quester%", "%npc%")); } } } pack.getConversation(convName).saveConfig(); } } } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } Debug.broadcast("Changed %quester% variables to %npc%"); config.set("version", "v27"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v25() { try { for (ConfigPackage pack : Config.getPackages().values()) { String packName = pack.getName(); FileConfiguration events = pack.getEvents().getConfig(); for (String key : events.getKeys(false)) { String event = events.getString(key); if (event.startsWith("journal ")) { events.set(key, "journal add " + event.substring(8)); } } pack.getEvents().saveConfig(); } } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } Debug.broadcast("Added \"add\" keyword to journal events"); config.set("version", "v26"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v24() { Debug.broadcast("Added prefix to language files"); config.set("version", "v25"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v23() { try { Debug.info("Adding option to disable mcMMO hooking to the config"); config.set("hook.mcmmo", "true"); } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } Debug.broadcast("Added mcMMO compatibility"); config.set("version", "v24"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v22() { Debug.broadcast("Added Dutch translation"); config.set("version", "v23"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v21() { try { Debug.info("Updating the database"); Connection con = instance.getDB().getConnection(); String prefix = Config.getString("config.mysql.prefix"); // update database format Debug.info("Adding conversation column to player table"); if (instance.isMySQLUsed()) { con.prepareStatement( "ALTER TABLE " + prefix + "player ADD conversation VARCHAR(512) AFTER language;") .executeUpdate(); } else { con.prepareStatement("BEGIN TRANSACTION").executeUpdate(); con.prepareStatement("ALTER TABLE " + prefix + "player RENAME TO " + prefix + "player_old") .executeUpdate(); con.prepareStatement("CREATE TABLE IF NOT EXISTS " + prefix + "player (id INTEGER PRIMARY KEY AUTOINCREMENT, playerID" + " VARCHAR(256) NOT NULL, language VARCHAR(16) NOT NULL, conversation VARCHAR(512));") .executeUpdate(); con.prepareStatement("INSERT INTO " + prefix + "player SELECT id, playerID, language, 'null'" + " FROM " + prefix + "player_old").executeUpdate(); con.prepareStatement("COMMIT").executeUpdate(); } } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } Debug.broadcast("Added conversations to database format"); config.set("version", "v22"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v20() { try { ArrayList<ChatColor> npcColors = new ArrayList<>(); ArrayList<ChatColor> textColors = new ArrayList<>(); ArrayList<ChatColor> numberColors = new ArrayList<>(); ArrayList<ChatColor> optionColors = new ArrayList<>(); ArrayList<ChatColor> playerColors = new ArrayList<>(); ArrayList<ChatColor> answerColors = new ArrayList<>(); // get npc message format String npcFormat = config.getString("conversation.quester_line_format"); String[] npcParts = npcFormat.split("%quester%"); if (npcParts.length != 2) { Debug.info("Could not parse NPC text format, saving defaults"); npcColors.add(ChatColor.DARK_RED); textColors.add(ChatColor.GREEN); textColors.add(ChatColor.ITALIC); } else { try { for (String code : npcParts[0].split("&")) { if (code.length() < 1) continue; npcColors.add(ChatColor.getByChar(code.charAt(0))); } for (String code : npcParts[1].split("&")) { if (code.length() < 1) continue; textColors.add(ChatColor.getByChar(code.charAt(0))); } } catch (Exception e) { e.printStackTrace(); Debug.info("Could not parse NPC text format, saving defaults"); npcColors.add(ChatColor.DARK_RED); textColors.add(ChatColor.GREEN); textColors.add(ChatColor.ITALIC); } } // get player option format String optionFormat = config.getString("conversation.quester_reply_format"); String[] optionParts = optionFormat.split("%number%"); if (optionParts.length != 2) { Debug.info("Could not parse player option format, saving defaults"); numberColors.add(ChatColor.YELLOW); optionColors.add(ChatColor.AQUA); } else { try { for (String code : optionParts[0].split("&")) { if (code.length() < 1) continue; numberColors.add(ChatColor.getByChar(code.charAt(0))); } for (String code : optionParts[1].split("&")) { if (code.length() < 1) continue; optionColors.add(ChatColor.getByChar(code.charAt(0))); } } catch (Exception e) { e.printStackTrace(); Debug.info("Could not parse player option format, saving defaults"); numberColors.add(ChatColor.YELLOW); optionColors.add(ChatColor.AQUA); } } // get player answer format String answerFormat = config.getString("conversation.player_reply_format"); String[] answerParts = answerFormat.split("%player%"); if (answerParts.length != 2) { Debug.info("Could not parse player answer format, saving defaults"); playerColors.add(ChatColor.DARK_GREEN); answerColors.add(ChatColor.GRAY); } else { try { for (String code : answerParts[0].split("&")) { if (code.length() < 1) continue; playerColors.add(ChatColor.getByChar(code.charAt(0))); } for (String code : answerParts[1].split("&")) { if (code.length() < 1) continue; answerColors.add(ChatColor.getByChar(code.charAt(0))); } } catch (Exception e) { e.printStackTrace(); Debug.info("Could not parse player answer format, saving defaults"); playerColors.add(ChatColor.DARK_GREEN); answerColors.add(ChatColor.GRAY); } } StringBuilder npc = new StringBuilder(); StringBuilder text = new StringBuilder(); StringBuilder number = new StringBuilder(); StringBuilder option = new StringBuilder(); StringBuilder player = new StringBuilder(); StringBuilder answer = new StringBuilder(); for (ChatColor color : npcColors) { if (color == null) continue; npc.append(color.name().toLowerCase() + ","); } config.set("conversation_colors.npc", npc.substring(0, npc.length() - 1)); for (ChatColor color : textColors) { if (color == null) continue; text.append(color.name().toLowerCase() + ","); } config.set("conversation_colors.text", text.substring(0, text.length() - 1)); for (ChatColor color : numberColors) { if (color == null) continue; number.append(color.name().toLowerCase() + ","); } config.set("conversation_colors.number", number.substring(0, number.length() - 1)); for (ChatColor color : optionColors) { if (color == null) continue; option.append(color.name().toLowerCase() + ","); } config.set("conversation_colors.option", option.substring(0, option.length() - 1)); for (ChatColor color : playerColors) { if (color == null) continue; player.append(color.name().toLowerCase() + ","); } config.set("conversation_colors.player", player.substring(0, player.length() - 1)); for (ChatColor color : answerColors) { if (color == null) continue; answer.append(color.name().toLowerCase() + ","); } config.set("conversation_colors.answer", answer.substring(0, answer.length() - 1)); config.set("conversation", null); } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } Debug.broadcast("Converted conversation format strings to colors"); config.set("version", "v21"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v19() { try { if (config.getString("tellraw").equalsIgnoreCase("true")) { config.set("default_conversation_IO", "tellraw"); } else { config.set("default_conversation_IO", "simple"); } config.set("tellraw", null); FileConfiguration messages = Config.getMessages().getConfig(); String message; message = messages.getString("global.quester_line_format"); if (message == null) message = "&4%quester%&f: &a&o"; config.set("conversation.quester_line_format", message); message = messages.getString("global.quester_reply_format"); if (message == null) message = "&e%number%. &b"; config.set("conversation.quester_reply_format", message); message = messages.getString("global.player_reply_format"); if (message == null) message = "&2%player%&f: &7"; config.set("conversation.player_reply_format", message); message = messages.getString("global.date_format"); if (message == null) message = "dd.MM.yyyy HH:mm"; config.set("date_format", message); String cancel_color = messages.getString("global.cancel_color", "&2"); messages.set("global", null); Debug.broadcast("Moved 'global' messages to main config."); Config.getMessages().saveConfig(); for (ConfigPackage pack : Config.getPackages().values()) { String packName = pack.getName(); Debug.info("Processing " + packName + " package"); ConfigurationSection cancelers = pack.getMain().getConfig().getConfigurationSection("cancel"); for (String key : cancelers.getKeys(false)) { String canceler = cancelers.getString(key); StringBuilder string = new StringBuilder(); for (String part : canceler.split(" ")) { if (part.startsWith("name")) { string.append(part.replace(":", ":" + cancel_color) + " "); } else { string.append(part + " "); } } cancelers.set(key, string.toString().trim()); Debug.info(" Updated " + key + " canceler name color"); } pack.getMain().saveConfig(); for (String convName : pack.getConversationNames()) { ConfigAccessor conv = pack.getConversation(convName); conv.getConfig().set("unknown", null); conv.saveConfig(); Debug.info(" Removed 'unknown' messages from " + convName + " conversation"); } } Debug.broadcast("Removed no longer used 'unknown' message from conversations."); } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } config.set("version", "v20"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v18() { try { ConfigAccessor confMessages = Config.getMessages(); FileConfiguration messages = confMessages.getConfig(); for (String lang : messages.getKeys(false)) { if (lang.equalsIgnoreCase("global")) continue; Debug.info("Updating " + lang + " language"); try { messages.set(lang + ".purged", messages.getString(lang + ".purged").replace("%player%", "{1}")); messages.set(lang + ".item_created", messages.getString(lang + ".item_created").replace("%item%", "{1}")); messages.set(lang + ".player_event", messages.getString(lang + ".player_event").replace("%event%", "{1}")); messages.set(lang + ".player_condition", messages.getString(lang + ".player_condition") .replace("%condition%", "{1}").replace("%outcome%", "{2}")); messages.set(lang + ".quest_canceled", messages.getString(lang + ".quest_canceled").replace("%quest%", "{1}")); messages.set(lang + ".items_given", messages.getString(lang + ".items_given") .replace("%name%", "{1}").replace("%amount%", "{2}")); messages.set(lang + ".items_taken", messages.getString(lang + ".items_taken") .replace("%name%", "{1}").replace("%amount%", "{2}")); messages.set(lang + ".blocks_to_break", messages.getString(lang + ".blocks_to_break").replace("%amount%", "{1}")); messages.set(lang + ".blocks_to_place", messages.getString(lang + ".blocks_to_place").replace("%amount%", "{1}")); messages.set(lang + ".mobs_to_kill", messages.getString(lang + ".mobs_to_kill").replace("%amount%", "{1}")); messages.set(lang + ".conversation_start", messages.getString(lang + ".conversation_start").replace("%quester%", "{1}")); messages.set(lang + ".conversation_end", messages.getString(lang + ".conversation_end").replace("%quester%", "{1}")); } catch (NullPointerException e) { Debug.error( "The language " + lang + " is not present in the defaults, please update it manually."); } } confMessages.saveConfig(); Debug.broadcast("Updated messages to new replace format"); } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } config.set("version", "v19"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v17() { try { for (ConfigPackage pack : Config.getPackages().values()) { String packName = pack.getName(); ConfigAccessor main = pack.getMain(); main.getConfig().set("tag_point_prefix", "false"); main.saveConfig(); } Debug.broadcast("Added prefix option to all packages."); } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } config.set("version", "v18"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v16() { try { // move objectives from events.yml to objectives.yml Debug.info("Moving objectives to objectives.yml"); for (ConfigPackage pack : Config.getPackages().values()) { String packName = pack.getName(); Debug.info(" Package " + packName); ConfigAccessor events = pack.getEvents(); ConfigAccessor objectives = pack.getObjectives(); ConfigAccessor main = pack.getMain(); for (String event : events.getConfig().getKeys(false)) { // extract label and build the new instruction int i = 0; // counts unnamed objectives String instruction = events.getConfig().getString(event); if (instruction.startsWith("objective ")) { Debug.info(" Starting event " + event); String[] parts = instruction.substring(10).split(" "); StringBuilder string = new StringBuilder(); String label = null; String conditions = ""; for (String part : parts) { if (part.startsWith("label:")) { label = part.substring(6); } else if (part.startsWith("event_conditions:")) { conditions = part; } else if (parts[0].equalsIgnoreCase("delay") && part.startsWith("delay:")) { string.append(part.substring(6)); string.append(' '); } else { string.append(part); string.append(' '); } } String newInstruction = string.toString().trim(); // if label is not present, skip this one if (label == null) { Debug.info(" There is no label, generating one"); label = "objective" + i; i++; } Debug.info(" Saving the objective as " + label + ", instruction: " + newInstruction); // save objective and generate label objectives.getConfig().set(label, newInstruction); events.getConfig().set(event, ("objective start " + label + " " + conditions).trim()); } else if (instruction.startsWith("delete ")) { // update delete events Debug.info(" Delete event " + event); events.getConfig().set(event, "objective " + instruction); } } // rename event_conditions to conditions for (String event : events.getConfig().getKeys(false)) { String instruction = events.getConfig().getString(event); events.getConfig().set(event, instruction.replace("event_conditions:", "conditions:")); } // update global locations String raw = main.getConfig().getString("global_locations"); if (raw != null && !raw.equals("")) { StringBuilder string = new StringBuilder(); String[] parts = raw.split(","); for (String event : parts) { String inst = events.getConfig().getString(event); if (inst == null) { continue; } String[] instParts = inst.split(" "); if (instParts.length > 2 && inst.startsWith("objective start ")) { string.append(instParts[2] + ","); } } main.getConfig().set("global_locations", string.substring(0, string.length() - 1)); } events.saveConfig(); objectives.saveConfig(); main.saveConfig(); } Debug.broadcast("Moved objectives to a separate file and renamed" + " 'event_conditions:' argument to 'conditions:'"); Debug.info("Updating the database"); Connection con = instance.getDB().getConnection(); String prefix = Config.getString("config.mysql.prefix"); // update database format Debug.info("Updating the database format"); if (instance.isMySQLUsed()) { con.prepareStatement( "ALTER TABLE " + prefix + "objectives ADD objective VARCHAR(512) NOT NULL AFTER playerID;") .executeUpdate(); } else { con.prepareStatement("BEGIN TRANSACTION").executeUpdate(); con.prepareStatement("ALTER TABLE " + prefix + "objectives RENAME TO " + prefix + "objectives_old") .executeUpdate(); con.prepareStatement( "CREATE TABLE IF NOT EXISTS " + prefix + "objectives (id INTEGER PRIMARY KEY AUTOINCREMENT," + " playerID VARCHAR(256) NOT NULL, objective VARCHAR(512)" + " NOT NULL, instructions VARCHAR(2048) NOT NULL);") .executeUpdate(); con.prepareStatement("INSERT INTO " + prefix + "objectives" + " SELECT id, playerID, 'null', instructions FROM " + prefix + "objectives_old") .executeUpdate(); con.prepareStatement("COMMIT").executeUpdate(); } // update each entry Debug.info("Updating entries"); ResultSet res = con.prepareStatement("SELECT * FROM " + prefix + "objectives").executeQuery(); while (res.next()) { String oldInst = res.getString("instructions"); Debug.info(" Loaded instruction: " + oldInst); String label = null; String[] parts = oldInst.split(" "); String newInst; for (String part : parts) { if (part.startsWith("label:")) { label = part.substring(6); break; } } if (label == null) { Debug.info(" The objective without label, removing"); PreparedStatement stmt = con .prepareStatement("DELETE FROM " + prefix + "objectives WHERE id = ?"); stmt.setInt(1, res.getInt("id")); stmt.executeUpdate(); continue; } // attack correct package in front of the label for (ConfigPackage pack : Config.getPackages().values()) { String packName = pack.getName(); if (pack.getObjectives().getConfig().contains(label)) { label = packName + "." + label; break; } } try { switch (parts[0].toLowerCase()) { case "tame": case "block": case "smelt": case "craft": case "mobkill": newInst = parts[2]; break; case "delay": newInst = parts[1].substring(6); break; case "npckill": case "mmobkill": newInst = parts[2].substring(7); break; default: newInst = ""; } } catch (ArrayIndexOutOfBoundsException e) { Debug.info(" Could not read data from objective " + label + ", removing"); PreparedStatement stmt = con .prepareStatement("DELETE FROM " + prefix + "objectives WHERE id = ?"); stmt.setInt(1, res.getInt("id")); stmt.executeUpdate(); continue; } Debug.info(" Updating the " + label + " objective: '" + newInst + "'"); PreparedStatement stmt = con.prepareStatement( "UPDATE " + prefix + "objectives SET objective=?, instructions=? WHERE id = ?"); stmt.setString(1, label); stmt.setString(2, newInst); stmt.setInt(3, res.getInt("id")); stmt.executeUpdate(); } Debug.broadcast("Updated objective instruction strings in the database"); } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } config.set("version", "v17"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v15() { try { config.set("remove_items_after_respawn", "true"); } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } config.set("version", "v16"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v14() { try { if (config.getString("uuid").equals("false")) { convertNamesToUUID(); } config.set("default_package", "default"); config.set("cmd_blacklist", new String[] { "spawn" }); config.set("uuid", null); config.set("metrics", null); config.set("hook.citizens", "true"); config.set("hook.mythicmobs", "true"); config.set("hook.vault", "true"); config.set("hook.worldguard", "true"); config.set("hook.skript", "true"); Debug.broadcast("Added default_package, hook and cmd_blacklist" + " options to main config, removed metrics and uuid!"); } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } config.set("version", "v15"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v13() { try { Debug.info("Removing empty lines in conversation files"); for (ConfigPackage pack : Config.getPackages().values()) { String packName = pack.getName(); Debug.info(" Package " + packName); for (String convName : pack.getConversationNames()) { Debug.info(" Conversation " + convName); ConfigAccessor conv = pack.getConversation(convName); for (String key : conv.getConfig().getKeys(true)) { if (conv.getConfig().getString(key).equals("")) { Debug.info(" Key removed: " + key); conv.getConfig().set(key, null); } } conv.saveConfig(); } } } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } Debug.broadcast("Removed unnecessary empty lines in conversation config files."); config.set("version", "v14"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v12() { try { Debug.info("Moving all configuration to \"default\" package"); // clear the default package, which contains only default quest File defPkg = Config.getPackages().get("default").getFolder(); Debug.info(" Deleting default files"); for (File file : defPkg.listFiles()) { file.delete(); } // move files that can be moved without modifications File root = instance.getDataFolder(); String[] filesToMove = new String[] { "events", "conditions", "items", "journal" }; for (String fileToMove : filesToMove) { Debug.info(" Moving " + fileToMove + ".yml file"); new File(root, fileToMove + ".yml").renameTo(new File(defPkg, fileToMove + ".yml")); } // move all conversations File newConversationFolder = new File(defPkg, "conversations"); File oldConversationFolder = new File(root, "conversations"); newConversationFolder.mkdir(); for (File conversation : oldConversationFolder.listFiles()) { Debug.info(" Moving " + conversation.getName() + " conversation file"); conversation.renameTo(new File(newConversationFolder, conversation.getName())); } // generate main.yml file Debug.info(" Generating main.yml file"); File mainFile = new File(defPkg, "main.yml"); mainFile.createNewFile(); FileConfiguration main = YamlConfiguration.loadConfiguration(mainFile); // copy the data String globalLocations = config.getString("global_locations"); ConfigurationSection staticEvents = config.getConfigurationSection("static"); ConfigurationSection npcs = ch.getConfigs().get("npcs").getConfig().getRoot(); main.set("global_locations", globalLocations); if (staticEvents != null) { for (String key : staticEvents.getKeys(false)) { main.set("static." + key, staticEvents.getString(key)); } } if (npcs != null) { for (String key : npcs.getKeys(false)) { main.set("npcs." + key, npcs.getString(key)); } for (File conv : newConversationFolder.listFiles()) { main.set("npcs." + conv.getName().replace(".yml", ""), conv.getName().replace(".yml", "")); } } main.save(mainFile); // remove old values from configuration Debug.info(" Removing old files and config values"); oldConversationFolder.delete(); config.set("global_locations", null); config.set("static", null); new File(root, "npcs.yml").delete(); Debug.info("Configuration updated!"); Debug.broadcast("Updating the database, it may take a long time!"); Connection con = instance.getDB().getConnection(); String prefix = instance.getConfig().getString("mysql.prefix", ""); ResultSet res = con.createStatement().executeQuery("SELECT * FROM " + prefix + "objectives"); ArrayList<String[]> objectives = new ArrayList<>(); // iterate over every objective string in the database while (res.next()) { String[] parts = res.getString("instructions").split(" "); StringBuilder newInstruction = new StringBuilder(); for (String part : parts) { if (part.startsWith("events:")) { newInstruction.append("events:"); String[] events = part.substring(7).split(","); for (String event : events) { newInstruction.append("default." + event + ","); } newInstruction.deleteCharAt(newInstruction.length() - 1); } else if (part.startsWith("conditions:")) { newInstruction.append("conditions:"); String[] conditions = part.substring(11).split(","); for (String condition : conditions) { newInstruction.append("default." + condition + ","); } newInstruction.deleteCharAt(newInstruction.length() - 1); } else { newInstruction.append(part); } newInstruction.append(' '); } objectives.add(new String[] { res.getString("playerID"), newInstruction.toString().trim() }); } res = con.createStatement().executeQuery("SELECT * FROM " + prefix + "journal"); ArrayList<String[]> pointers = new ArrayList<>(); // iterate over every journal pointer in the database while (res.next()) { pointers.add(new String[] { res.getString("playerID"), "default." + res.getString("pointer"), res.getString("date") }); } con.createStatement().executeUpdate("DELETE FROM " + prefix + "objectives"); con.createStatement().executeUpdate("DELETE FROM " + prefix + "journal"); for (String[] objective : objectives) { PreparedStatement stmt = con.prepareStatement( "INSERT INTO " + prefix + "objectives (playerID, instructions) VALUES (?,?)"); stmt.setString(1, objective[0]); stmt.setString(2, objective[1]); stmt.executeUpdate(); } for (String[] pointer : pointers) { PreparedStatement stmt = con.prepareStatement( "INSERT INTO " + prefix + "journal (playerID, pointer, date) VALUES (?,?,?)"); stmt.setString(1, pointer[0]); stmt.setString(2, pointer[1]); stmt.setString(3, pointer[2]); stmt.executeUpdate(); } Debug.info("Done! Everything converted."); } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } Debug.broadcast("Introduced new packaging system and moved configuration to \"default\" package!"); config.set("version", "v13"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v11() { try { Debug.info("Updating objectives in configuration"); ConfigAccessor events = ch.getConfigs().get("events"); ArrayList<String> labels = new ArrayList<>(); boolean notified = false; // for every event check if it's objective for (String key : events.getConfig().getKeys(false)) { String value = events.getConfig().getString(key); if (value.startsWith("objective ")) { Debug.info(" Found " + key + " objective event"); // replace "tag:" with "label:" in all found objectives String[] parts = value.split(" "); StringBuilder builder = new StringBuilder(); for (int i = 0; i < parts.length; i++) { if (parts[i].startsWith("tag:")) { String label = parts[i].substring(4); if (!notified && labels.contains(label)) { notified = true; Debug.error("You have multiple objectives with the same label!" + " That is an error, because the player cannot have" + " active more than one objective with the same label"); } labels.add(label); parts[i] = "label:" + label; } builder.append(parts[i]); builder.append(" "); } String newValue = builder.toString().trim(); Debug.info(" After processing: " + newValue); events.getConfig().set(key, newValue); } } events.saveConfig(); Debug.info("Converted all objectives in configuration"); // update all objectives in the database Debug.broadcast("Converting objectives in the database, it may take a long time"); Connection con = instance.getDB().getConnection(); String prefix = instance.getConfig().getString("mysql.prefix", ""); ResultSet res = con.createStatement().executeQuery("SELECT * FROM " + prefix + "objectives"); HashMap<String, ArrayList<String>> objectives = new HashMap<>(); HashMap<String, ArrayList<String>> labels2 = new HashMap<>(); // iterate over every objective string in the database while (res.next()) { String playerID = res.getString("playerID"); String objective = res.getString("instructions"); String label = null; for (String part : objective.split(" ")) { if (part.startsWith("tag:")) { label = part.substring(4); } } if (label == null) { Debug.info(" Found objective without a label, that's strange... Anyway, skipping. Player: " + playerID); continue; } Debug.info(" Found objective for player " + playerID + " with label " + label); ArrayList<String> oList = objectives.get(playerID); ArrayList<String> lList = labels2.get(playerID); if (oList == null) { oList = new ArrayList<>(); lList = new ArrayList<>(); } // cannot have two objectives with the same tag if (lList.contains(label)) { Debug.info(" Label already exists, skipping this one!"); continue; } String converted = convertObjective(objective); Debug.info(" Objective converted: " + converted); oList.add(converted); lList.add(label); objectives.put(playerID, oList); labels2.put(playerID, lList); } // everything is extracted from the database and converted // time to put it back Debug.info("Inserting everything into the database..."); con.createStatement().executeUpdate("DELETE FROM " + prefix + "objectives"); for (String playerID : objectives.keySet()) { for (String objective : objectives.get(playerID)) { PreparedStatement stmt = con.prepareStatement( "INSERT INTO " + prefix + "objectives (playerID, instructions) VALUES (?,?);"); stmt.setString(1, playerID); stmt.setString(2, objective); stmt.executeUpdate(); } } Debug.info("Done! Everything converted"); } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } Debug.broadcast("Changed keyword \"tag:\" to \"label:\" in all objectives!"); config.set("version", "v12"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v10() { try { Debug.info("Updating instruction strings"); Debug.info(" Updating conditions"); ConfigAccessor conditions = ch.getConfigs().get("conditions"); conditions: for (String key : conditions.getConfig().getKeys(false)) { Debug.info(" Processing " + key + " condition"); String instruction = conditions.getConfig().getString(key).trim(); String[] parts = instruction.split(" "); String type = parts[0].toLowerCase(); ArrayList<String> newParts = new ArrayList<>(); newParts.add(type); switch (type) { case "hand": Debug.info(" Found hand type"); String item = null; for (String part : parts) { if (part.startsWith("item:")) { item = part.substring(5); } } if (item != null) { newParts.add(item); } else { Debug.info(" There is no item defined, skipping"); continue conditions; } break; case "or": case "and": Debug.info(" Found or/and type"); String orAndConditions = null; for (String part : parts) { if (part.startsWith("conditions:")) { orAndConditions = part.substring(11); } } if (orAndConditions != null) { newParts.add(orAndConditions); } else { Debug.info(" There are no conditions defined, skipping"); continue conditions; } break; case "location": Debug.info(" Found location type"); String location = null; for (String part : parts) { if (part.startsWith("loc:")) { location = part.substring(4); } } if (location != null) { newParts.add(location); } else { Debug.info(" There is no location defined, skipping"); continue conditions; } break; case "health": Debug.info(" Found health type"); String health = null; for (String part : parts) { if (part.startsWith("health:")) { health = part.substring(7); } } if (health != null) { newParts.add(health); } else { Debug.info(" There is no health amount defined, skipping"); continue conditions; } break; case "experience": Debug.info(" Found experience type"); String exp = null; for (String part : parts) { if (part.startsWith("exp:")) { exp = part.substring(4); } } if (exp != null) { newParts.add(exp); } else { Debug.info(" There is no experience level defined, skipping"); continue conditions; } break; case "permission": Debug.info(" Found permission type"); String perm = null; for (String part : parts) { if (part.contains("perm:")) { perm = part.substring(5); } } if (perm != null) { newParts.add(perm); } else { Debug.info(" There is no permission defined, skipping"); continue conditions; } break; case "point": Debug.info(" Found point type"); String category = null; String amount = null; for (String part : parts) { if (part.startsWith("category:")) { category = part.substring(9); } else if (part.startsWith("count:")) { amount = part.substring(6); } } if (category != null && amount != null) { newParts.add(category); newParts.add(amount); } else { Debug.info(" There is no category/amount defined, skipping"); continue conditions; } break; case "tag": Debug.info(" Found tag type"); String tag = null; for (String part : parts) { if (part.startsWith("tag:")) { tag = part.substring(4); } } if (tag != null) { newParts.add(tag); } else { Debug.info(" There is no tag defined, skipping"); continue conditions; } break; case "armor": Debug.info(" Found armor type"); String material = null; String armorType = null; String enchants = null; for (String part : parts) { if (part.startsWith("material:")) { material = part.substring(9); } if (part.startsWith("type:")) { armorType = part.substring(5); } if (part.startsWith("enchants:")) { enchants = part; } } if (material != null && type != null) { Material armor = null; try { armor = Material.matchMaterial(material + "_" + armorType); } catch (Exception e) { Debug.info(" Could not read armor type, skipping"); continue conditions; } String itemInstruction = armor.toString(); if (enchants != null) { itemInstruction = itemInstruction + " " + enchants; } ConfigAccessor itemsConfig = ch.getConfigs().get("items"); int i = 0; while (itemsConfig.getConfig().contains("armor" + i)) { i++; } itemsConfig.getConfig().set("armor" + i, itemInstruction); itemsConfig.saveConfig(); newParts.add("armor" + i); } else { Debug.info(" There is no armor defined, skipping"); continue conditions; } break; case "effect": Debug.info(" Found effect type"); String effect = null; for (String part : parts) { if (part.startsWith("type:")) { effect = part.substring(5); } } if (effect != null) { newParts.add(effect); } else { Debug.info(" There is no effect defined, skipping"); continue conditions; } break; case "time": Debug.info(" Found time type"); String time = null; for (String part : parts) { if (part.startsWith("time:")) { time = part.substring(5); } } if (time != null) { newParts.add(time); } else { Debug.info(" There is no time defined, skipping"); continue conditions; } break; case "weather": Debug.info(" Found weather type"); String weather = null; for (String part : parts) { if (part.startsWith("type:")) { weather = part.substring(5); } } if (weather != null) { newParts.add(weather); } else { Debug.info(" There is no weather defined, skipping"); continue conditions; } break; case "height": Debug.info(" Found height type"); String height = null; for (String part : parts) { if (part.startsWith("height:")) { height = part.substring(7); } } if (height != null) { newParts.add(height); } else { Debug.info(" There is no height defined, skipping"); continue conditions; } break; case "rating": Debug.info(" Found rating type"); String rating = null; for (String part : parts) { if (part.startsWith("rating:")) { rating = part.substring(7); } } if (rating != null) { newParts.add(rating); } else { Debug.info(" There is no rating defined, skipping"); continue conditions; } break; case "random": Debug.info(" Found random type"); String random = null; for (String part : parts) { if (part.startsWith("random:")) { random = part.substring(7); } } if (random != null) { newParts.add(random); } else { Debug.info(" There is no random defined, skipping"); continue conditions; } break; case "money": Debug.info(" Found money type"); String money = null; for (String part : parts) { if (part.startsWith("money:")) { money = part.substring(6); } } if (money != null) { newParts.add(money); } else { Debug.info(" There is no amount defined, skipping"); continue conditions; } break; default: Debug.info(" This one does not need updating"); continue conditions; } StringBuilder builder = new StringBuilder(); for (String part : newParts) { builder.append(part); builder.append(' '); } String newInstruction = builder.toString().trim(); Debug.info(" Processing done, instruction: '" + newInstruction + "'"); conditions.getConfig().set(key, newInstruction); } Debug.info(" All conditions updated successfully, saving to the file"); conditions.saveConfig(); Debug.info(" Updating events"); ConfigAccessor events = ch.getConfigs().get("events"); events: for (String key : events.getConfig().getKeys(false)) { Debug.info(" Processing " + key + " event"); String instruction = events.getConfig().getString(key).trim(); String[] parts = instruction.split(" "); String type = parts[0].toLowerCase(); ArrayList<String> newParts = new ArrayList<>(); newParts.add(type); switch (type) { case "folder": Debug.info(" Found folder type"); String folderEvents = null; String delay = null; String random = null; for (String part : parts) { if (part.startsWith("events:")) { folderEvents = part.substring(7); } if (part.startsWith("delay:")) { delay = part; } if (part.startsWith("random:")) { random = part; } } if (events != null) { newParts.add(folderEvents); if (delay != null) { newParts.add(delay); } if (random != null) { newParts.add(random); } } else { Debug.info(" There are no events defined, skipping"); continue events; } break; case "setblock": Debug.info(" Found setblock type"); String block = null; String loc = null; String data = null; for (String part : parts) { if (part.startsWith("block:")) { block = part.substring(6); } if (part.startsWith("loc:")) { loc = part.substring(4); } if (part.startsWith("data:")) { data = part; } } if (block != null && loc != null) { newParts.add(block); newParts.add(loc); if (data != null) { newParts.add(data); } } else { Debug.info(" There is no block/location defined, skipping"); continue events; } break; default: Debug.info(" This one does not need updating"); continue events; } StringBuilder builder = new StringBuilder(); for (String part : newParts) { builder.append(part); builder.append(' '); } String newInstruction = builder.toString().trim(); Debug.info(" Processing done, instruction: '" + newInstruction + "'"); events.getConfig().set(key, newInstruction); } Debug.info(" All events updated successfully, saving to the file"); events.saveConfig(); } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } Debug.broadcast("Made instruction strings more beautiful! Please read the documentation again."); config.set("version", "v11"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v9() { config.set("combat_delay", "10"); config.set("notify_pullback", "false"); Debug.broadcast("Added combat delay and pullback notify options!"); config.set("version", "v10"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v8() { config.set("version", "v9"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v7() { ConfigAccessor messages = ch.getConfigs().get("messages"); messages.getConfig().set("global.date_format", "dd.MM.yyyy HH:mm"); messages.saveConfig(); Debug.broadcast("Added date format line to messages.yml"); config.set("version", "v8"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v6() { Debug.broadcast("Added backpacks to the database!"); config.set("version", "v7"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v5() { try { // delete isused column from tables objectives and tags Database database = instance.getDB(); Connection connection = database.getConnection(); String[] tables = new String[] { "objectives", "tags" }; String prefix = instance.getConfig().getString("mysql.prefix", ""); if (instance.isMySQLUsed()) { connection.prepareStatement("ALTER TABLE " + prefix + "objectives DROP COLUMN isused;") .executeUpdate(); connection.prepareStatement("ALTER TABLE " + prefix + "tags DROP COLUMN isused;").executeUpdate(); } else { // drop column from objectives connection.prepareStatement("BEGIN TRANSACTION").executeUpdate(); connection .prepareStatement( "ALTER TABLE " + prefix + "objectives RENAME TO " + prefix + "objectives_old") .executeUpdate(); connection.prepareStatement("CREATE TABLE IF NOT EXISTS " + prefix + "objectives" + " (id INTEGER PRIMARY KEY AUTOINCREMENT, playerID VARCHAR(256) NOT NULL, " + "instructions VARCHAR(2048) NOT NULL);").executeUpdate(); connection.prepareStatement("INSERT INTO " + prefix + "objectives SELECT id, " + "playerID, instructions FROM " + prefix + "objectives_old").executeUpdate(); connection.prepareStatement("DROP TABLE " + prefix + "objectives_old").executeUpdate(); connection.prepareStatement("COMMIT").executeUpdate(); // drop column from tags connection.prepareStatement("BEGIN TRANSACTION").executeUpdate(); connection.prepareStatement("ALTER TABLE " + prefix + "tags RENAME TO " + prefix + "tags_old") .executeUpdate(); connection.prepareStatement("CREATE TABLE IF NOT EXISTS " + prefix + "tags" + " (id INTEGER PRIMARY KEY AUTOINCREMENT, playerID VARCHAR(256) NOT NULL, " + "tag TEXT NOT NULL);").executeUpdate(); connection.prepareStatement( "INSERT INTO " + prefix + "tags SELECT id, playerID, tag FROM " + prefix + "tags_old") .executeUpdate(); connection.prepareStatement("DROP TABLE " + prefix + "tags_old").executeUpdate(); connection.prepareStatement("COMMIT").executeUpdate(); } Debug.broadcast("Updated database format to better one."); } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } config.set("version", "v6"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v4() { try { // update all give/take events and item condition to match new // parser ConfigAccessor eventsAccessor = ch.getConfigs().get("events"); FileConfiguration eventsConfig = eventsAccessor.getConfig(); Debug.info("Updating events!"); // check every event in configuration for (String key : eventsConfig.getKeys(false)) { Debug.info(" Processing " + key); String instruction = eventsConfig.getString(key); // if the event is of type "give" or "take" then proceed if (instruction.startsWith("give ") || instruction.startsWith("take ")) { String[] parts = instruction.split(" "); Debug.info(" Found " + parts[0] + " event"); // get item's amount int amount = 1; for (String part : parts) { if (part.startsWith("amount:")) { amount = Integer.parseInt(part.substring(7)); Debug.info(" Amount is set to " + amount); } } // generate new instruction String newInstruction = parts[0] + " " + parts[1] + ((amount != 1) ? ":" + amount : ""); Debug.info(" Saving instruction '" + newInstruction + "'"); // save it eventsConfig.set(key, newInstruction); } } // when all events are converted, save the file eventsAccessor.saveConfig(); // update all item conditions ConfigAccessor conditionsAccessor = ch.getConfigs().get("conditions"); FileConfiguration conditionsConfig = conditionsAccessor.getConfig(); Debug.info("Updatng conditions!"); // check every condition in configuration for (String key : conditionsConfig.getKeys(false)) { Debug.info(" Processing " + key); String instruction = conditionsConfig.getString(key); // if the condition is of type "item" then proceed if (instruction.startsWith("item ")) { String[] parts = instruction.split(" "); Debug.info(" Found item condition"); // get item name and amount String name = null; int amount = 1; for (String part : parts) { if (part.startsWith("item:")) { name = part.substring(5); Debug.info(" Name is " + name); } else if (part.startsWith("amount:")) { amount = Integer.parseInt(part.substring(7)); Debug.info(" Amount is " + amount); } } // generate new instruction String newInstruction = "item " + name + ((amount != 1) ? ":" + amount : ""); Debug.info(" Saving instruction '" + newInstruction + "'"); // save it conditionsConfig.set(key, newInstruction); } } // when all conditions are converted, save the file conditionsAccessor.saveConfig(); Debug.broadcast("Converted give/take events and item conditions to new format!"); } catch (Exception e) { e.printStackTrace(); Debug.error(ERROR); } config.set("version", "v5"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v3() { config.set("mysql.prefix", ""); Debug.broadcast("Added prefix option to MySQL settings!"); config.set("version", "v4"); instance.saveConfig(); } @SuppressWarnings("unused") private void update_from_v2() { try { // start time counting, because why not? long time = new Date().getTime(); // Get all conditions with --inverted tag into the map // <name,instruction> without --inverted tag and remove them form // config ConfigAccessor conditionsAccessor = ch.getConfigs().get("conditions"); FileConfiguration conditionsConfig = conditionsAccessor.getConfig(); // at the beginning trim all conditions, so they won't get // confused later on for (String path : conditionsConfig.getKeys(false)) { conditionsConfig.set(path, conditionsConfig.getString(path).trim()); } HashMap<String, String> conditionsInverted = new HashMap<>(); Debug.info("Extracting conditions to a map"); // for each condition for (String name : conditionsConfig.getKeys(false)) { // get instruction String condition = conditionsConfig.getString(name); boolean wasInverted = false; int i = 1; Debug.info(" Checking condition " + name); // if it is --inverted while (condition.contains("--inverted")) { Debug.info(" Loop " + i); i++; Debug.info(" Instruction: '" + condition + "'"); // get starting index of --inverted int startingIndex = condition.indexOf(" --inverted"); Debug.info(" First occurence of --inverted tag: " + startingIndex); // get first half (to cut --inverted) String firstHalf = condition.substring(0, startingIndex); Debug.info(" First half is '" + firstHalf + "'"); // get last half (from the end of --inverted string) String lastHalf = condition.substring(startingIndex + 11); Debug.info(" Last half is '" + lastHalf + "'"); // get new condition string without --inverted tag condition = firstHalf + lastHalf; wasInverted = true; Debug.info(" And the whole new condition is '" + condition + "'"); } if (wasInverted) { Debug.info(" Removing from config and putting into a map!"); // remove it from config conditionsConfig.set(name, null); // put it into the map conditionsInverted.put(name, condition); } } // for each, check for duplicates Debug.info("Checking for duplicates in config"); HashMap<String, String> nameChanging = new HashMap<>(); for (String invertedName : conditionsInverted.keySet()) { // check every condition from the map Debug.info(" Checking condition " + invertedName); String duplicateName = null; for (String normalName : conditionsConfig.getKeys(false)) { // against every condition that is still in the config if (conditionsConfig.getString(normalName).equals(conditionsInverted.get(invertedName))) { // if it is the same, then we have a match; we need to // mark it as a duplicate Debug.info(" Found a duplicate: " + normalName); duplicateName = normalName; } } if (duplicateName != null) { // if it still exists in config, put it into map <old // name, new name> as duplicate and !original Debug.info( " Inserting into name changing map, from " + invertedName + " to !" + duplicateName); nameChanging.put(invertedName, "!" + duplicateName); } else { // if it doesn't, put into a map as original and !original, // and reinsert into config Debug.info( " Inserting into name changing map, from " + invertedName + " to !" + invertedName); Debug.info(" Readding to configuration!"); nameChanging.put(invertedName, "!" + invertedName); conditionsConfig.set(invertedName, conditionsInverted.get(invertedName)); } } Debug.info("Starting conditions updating!"); for (String key : conditionsConfig.getKeys(false)) { String instruction = conditionsConfig.getString(key).trim(); Debug.info(" Processing condition " + key); if (instruction.startsWith("or ") || instruction.startsWith("and ")) { String type = instruction.substring(0, instruction.indexOf(" ")); Debug.info(" Found " + type + " condition!"); int index = instruction.indexOf(" conditions:") + 12; String firstPart = instruction.substring(0, index); Debug.info(" First part is '" + firstPart + "'"); int secondIndex = index + instruction.substring(index).indexOf(" "); if (secondIndex <= index) { secondIndex = instruction.length(); } String conditionList = instruction.substring(index, secondIndex); Debug.info(" List of conditions is '" + conditionList + "'"); String lastPart = instruction.substring(secondIndex); Debug.info(" Last part is '" + lastPart + "'"); String[] parts = conditionList.split(","); for (int i = 0; i < parts.length; i++) { // check each of them if it should be replaced String replacement = nameChanging.get(parts[i]); if (replacement != null) { Debug.info(" Replacing " + parts[i] + " with " + replacement); parts[i] = replacement; } } StringBuilder newConditionsList = new StringBuilder(); for (String part : parts) { newConditionsList.append(part + ","); } String newInstruction = firstPart + newConditionsList.toString().substring(0, newConditionsList.length() - 1) + lastPart; Debug.info(" New instruction is '" + newInstruction + "'"); conditionsConfig.set(key, newInstruction); } } // save conditions so the changes persist conditionsAccessor.saveConfig(); // now we have a map with names which need to be changed across all // configuration; for each conversation, for each NPC option and // player option, replace old names from the map with new names Debug.info("Starting conversation updating"); // get every conversation accessor HashMap<String, ConfigAccessor> conversations = ch.getConversations(); for (String conversationName : conversations.keySet()) { Debug.info(" Processing conversation " + conversationName); ConfigAccessor conversation = conversations.get(conversationName); // this list will store every path to condition list in this // conversation List<String> paths = new ArrayList<>(); // for every npc option, check if it contains conditions // variable and add it to the list Debug.info(" Extracting conditions from NPC options"); ConfigurationSection npcOptions = conversation.getConfig().getConfigurationSection("NPC_options"); for (String npcPath : npcOptions.getKeys(false)) { String conditionPath = "NPC_options." + npcPath + ".conditions"; if (conversation.getConfig().isSet(conditionPath) && !conversation.getConfig().getString(conditionPath).equals("")) { Debug.info(" Adding " + conditionPath + " to the list"); paths.add(conditionPath); } } // for every player option, check if it contains conditions // variable and add it to the list Debug.info(" Extracting conditions from player options"); ConfigurationSection playerOptions = conversation.getConfig() .getConfigurationSection("player_options"); for (String playerPath : playerOptions.getKeys(false)) { String conditionPath = "player_options." + playerPath + ".conditions"; if (conversation.getConfig().isSet(conditionPath) && !conversation.getConfig().getString(conditionPath).equals("")) { Debug.info(" Adding " + conditionPath + " to the list"); paths.add(conditionPath); } } // now we have a list of valid paths to condition variables // in this conversation for (String path : paths) { Debug.info(" Processing path " + path); // get the list of conditions (as a single string, separated // by commas) String list = conversation.getConfig().getString(path); Debug.info(" Original conditions list is: " + list); // split it into an array String[] conditionArr = list.split(","); for (int i = 0; i < conditionArr.length; i++) { // for every condition name in array check if it should // be replaced String replacement = nameChanging.get(conditionArr[i]); if (replacement != null) { // and replace it Debug.info(" Replacing " + conditionArr[i] + " with " + replacement); conditionArr[i] = replacement; } } // now when everything is replaced generate new list (as a // single string) StringBuilder newListBuilder = new StringBuilder(); for (String condition : conditionArr) { newListBuilder.append(condition + ","); } String newList = newListBuilder.toString().substring(0, newListBuilder.length() - 1); Debug.info(" Saving new list: " + newList); // and set it conversation.getConfig().set(path, newList); } // save conversation so the changes persist conversation.saveConfig(); } // now every conversation is processed, time for events // for each event_conditions: and conditions: in events.yml, replace // old names from the map with new names Debug.info("Starting events updating"); ConfigAccessor eventsAccessor = ch.getConfigs().get("events"); for (String eventName : eventsAccessor.getConfig().getKeys(false)) { Debug.info(" Processing event " + eventName); // extract event's instruction String instruction = eventsAccessor.getConfig().getString(eventName); // check if it contains event conditions if (instruction.contains(" event_conditions:")) { Debug.info(" Found event conditions!"); // extract first half (to the start of condition list int index = instruction.indexOf(" event_conditions:") + 18; String firstHalf = instruction.substring(0, index); Debug.info(" First half is '" + firstHalf + "'"); // extract condition list int secondIndex = index + instruction.substring(index).indexOf(" "); if (secondIndex <= index) { secondIndex = instruction.length(); } String conditionList = instruction.substring(index, secondIndex); Debug.info(" Condition list is '" + conditionList + "'"); // extract last half (from the end of condition list) String lastHalf = instruction.substring(secondIndex, instruction.length()); Debug.info(" Last half is '" + lastHalf + "'"); // split conditions into an array String[] parts = conditionList.split(","); for (int i = 0; i < parts.length; i++) { // check each of them if it should be replaced String replacement = nameChanging.get(parts[i]); if (replacement != null) { Debug.info(" Replacing " + parts[i] + " with " + replacement); parts[i] = replacement; } } // put it all together StringBuilder newListBuilder = new StringBuilder(); for (String part : parts) { newListBuilder.append(part + ","); } String newList = newListBuilder.toString().substring(0, newListBuilder.length() - 1); Debug.info(" New condition list is '" + newList + "'"); // put the event together and save it String newEvent = firstHalf + newList + lastHalf; Debug.info(" Saving instruction '" + newEvent + "'"); eventsAccessor.getConfig().set(eventName, newEvent); } // read the instruction again, it could've changed instruction = eventsAccessor.getConfig().getString(eventName); // check if it containt objective conditions if (instruction.contains(" conditions:")) { Debug.info(" Found objective conditions!"); // extract first half (to the start of condition list int index = instruction.indexOf(" conditions:") + 12; String firstHalf = instruction.substring(0, index); Debug.info(" First half is '" + firstHalf + "'"); // extract condition list int secondIndex = index + instruction.substring(index).indexOf(" "); String conditionList = instruction.substring(index, secondIndex); Debug.info(" Condition list is '" + conditionList + "'"); // extract last half (from the end of condition list) String lastHalf = instruction.substring(secondIndex, instruction.length()); Debug.info(" Last half is '" + lastHalf + "'"); // split conditions into an array String[] parts = conditionList.split(","); for (int i = 0; i < parts.length; i++) { // check each of them if it should be replaced String replacement = nameChanging.get(parts[i]); if (replacement != null) { Debug.info(" Replacing " + parts[i] + " with " + replacement); parts[i] = replacement; } } // put it all together StringBuilder newListBuilder = new StringBuilder(); for (String part : parts) { newListBuilder.append(part + ","); } String newList = newListBuilder.toString().substring(0, newListBuilder.length() - 1); Debug.info(" New condition list is '" + newList + "'"); // put the event together and save it String newEvent = firstHalf + newList + lastHalf; Debug.info(" Saving instruction '" + newEvent + "'"); eventsAccessor.getConfig().set(eventName, newEvent); } // at this point we finished modifying this one event } // at this point we finished modifying every event, need to save // events eventsAccessor.saveConfig(); // every place where conditions are is now updated, finished! Debug.broadcast("Converted inverted conditions to a new format using exclamation marks!"); Debug.info("Converting took " + (new Date().getTime() - time) + "ms"); } catch (Exception e) { // try-catch block is required - if there is some exception, // the version wouldn't get changed and updater would fall into // an infinite loop of endless exceptiorns e.printStackTrace(); Debug.error(ERROR); } // set v3 version config.set("version", "v3"); instance.saveConfig(); // done } @SuppressWarnings("unused") private void update_from_v1() { config.set("debug", "false"); Debug.broadcast("Added debug option to configuration!"); config.set("version", "v2"); instance.saveConfig(); } private void updateTo1_6() { config.set("version", "v1"); instance.saveConfig(); performUpdate(); } private void updateTo1_5_3() { // nothing to update config.set("version", "1.5.3"); updateTo1_6(); } private void updateTo1_5_2() { // nothing to update config.set("version", "1.5.2"); updateTo1_5_3(); } private void updateTo1_5_1() { // nothing to update config.set("version", "1.5.1"); updateTo1_5_2(); } private void updateTo1_5() { Debug.broadcast("Started converting configuration files from v1.4 to v1.5!"); // add sound settings String[] array1 = new String[] { "start", "end", "journal", "update", "full" }; for (String string : array1) { config.set("sounds." + string, config.getDefaults().getString("sounds." + string)); } Debug.broadcast("Added new sound options!"); // add colors for journal String[] array2 = new String[] { "date.day", "date.hour", "line", "text" }; for (String string : array2) { config.set("journal_colors." + string, config.getDefaults().getString("journal_colors." + string)); } Debug.broadcast("Added new journal color options!"); // convert conditions in events to event_condition: format Debug.info("Starting updating 'conditions:' argument to 'event_conditions:' in events.yml"); ConfigAccessor events = ch.getConfigs().get("events"); for (String key : events.getConfig().getKeys(false)) { Debug.info(" Processing event " + key); if (events.getConfig().getString(key).contains("conditions:")) { StringBuilder parts = new StringBuilder(); for (String part : events.getConfig().getString(key).split(" ")) { if (part.startsWith("conditions:")) { parts.append("event_conditions:" + part.substring(11) + " "); } else { parts.append(part + " "); } } Debug.info(" Found 'conditions:' option, replacing!"); events.getConfig().set(key, parts.substring(0, parts.length() - 1)); } } Debug.broadcast("Events now use 'event_conditions:' for conditioning."); // convert objectives to new format Debug.info("Converting objectives to new format..."); ConfigAccessor objectives = ch.getConfigs().get("objectives"); for (String key : events.getConfig().getKeys(false)) { Debug.info(" Processing objective " + key); if (events.getConfig().getString(key).split(" ")[0].equalsIgnoreCase("objective")) { events.getConfig().set(key, "objective " + objectives.getConfig().getString(events.getConfig().getString(key).split(" ")[1])); Debug.info(" Event " + key + " converted!"); } } Debug.broadcast("Objectives converted to new, event-powered format!"); // convert global locations String globalLocations = config.getString("global_locations"); if (globalLocations != null && !globalLocations.equals("")) { StringBuilder configGlobalLocs = new StringBuilder(); Debug.broadcast("Converting global locations to use events..."); int i = 0; for (String globalLoc : config.getString("global_locations").split(",")) { i++; events.getConfig().set("global_location_" + i, "objective " + objectives.getConfig().getString(globalLoc)); configGlobalLocs.append("global_location_" + i + ","); Debug.broadcast("Converted " + globalLoc + " objective."); } config.set("global_locations", configGlobalLocs.substring(0, configGlobalLocs.length() - 1)); Debug.broadcast("All " + i + " global locations have been converted."); } events.saveConfig(); Debug.broadcast("Removing old file."); new File(instance.getDataFolder(), "objectives.yml").delete(); // convert books to new format Debug.broadcast("Converting books to new format!"); ConfigAccessor items = ch.getConfigs().get("items"); for (String key : items.getConfig().getKeys(false)) { String string = items.getConfig().getString(key); if (string.split(" ")[0].equalsIgnoreCase("WRITTEN_BOOK")) { String text = null; LinkedList<String> parts = new LinkedList<String>(Arrays.asList(string.split(" "))); for (Iterator<String> iterator = parts.iterator(); iterator.hasNext();) { String part = (String) iterator.next(); if (part.startsWith("text:")) { text = part.substring(5); iterator.remove(); break; } } if (text != null) { StringBuilder pages = new StringBuilder(); for (String page : Utils.pagesFromString(text.replace("_", " "))) { pages.append(page.replaceAll(" ", "_") + "|"); } parts.add("text:" + pages.substring(0, pages.length() - 2)); StringBuilder instruction = new StringBuilder(); for (String part : parts) { instruction.append(part + " "); } items.getConfig().set(key, instruction.toString().trim().replaceAll("\\n", "\\\\n")); Debug.broadcast("Converted book " + key + "."); } } } items.saveConfig(); Debug.broadcast("All books converted!"); // JournalBook.pagesFromString(questItem.getText(), false); config.set("tellraw", "false"); Debug.broadcast("Tellraw option added to config.yml!"); config.set("autoupdate", "true"); Debug.broadcast("AutoUpdater is now enabled by default! You can change this if you" + " want and reload the plugin, nothing will be downloaded in that case."); // end of update config.set("version", "1.5"); Debug.broadcast("Conversion to v1.5 finished."); updateTo1_5_1(); } private void updateTo1_4_3() { // nothing to update config.set("version", "1.4.3"); updateTo1_5(); } private void updateTo1_4_2() { // nothing to update config.set("version", "1.4.2"); updateTo1_4_3(); } private void updateTo1_4_1() { // nothing to update config.set("version", "1.4.1"); updateTo1_4_2(); } private void updateTo1_4() { Debug.broadcast("Started converting configuration files from v1.3 to v1.4!"); instance.getConfig().set("autoupdate", "false"); Debug.broadcast("Added AutoUpdate option to config. It's DISABLED by default!"); Debug.broadcast("Moving conversation to separate files..."); ConfigAccessor convOld = ch.getConfigs().get("conversations"); Set<String> keys = convOld.getConfig().getKeys(false); File folder = new File(instance.getDataFolder(), "conversations"); if (folder.exists() && folder.isDirectory()) for (File file : folder.listFiles()) { file.delete(); } for (String convID : keys) { File convFile = new File(folder, convID + ".yml"); Map<String, Object> convSection = convOld.getConfig().getConfigurationSection(convID).getValues(true); YamlConfiguration convNew = YamlConfiguration.loadConfiguration(convFile); for (String key : convSection.keySet()) { convNew.set(key, convSection.get(key)); } try { convNew.save(convFile); Debug.broadcast("Conversation " + convID + " moved to it's own file!"); } catch (IOException e) { e.printStackTrace(); } } Debug.broadcast("All conversations moved, deleting old file."); new File(instance.getDataFolder(), "conversations.yml").delete(); // updating items Debug.broadcast("Starting conversion of items..."); // this map will contain all QuestItem objects extracted from // configs HashMap<String, QuestItem> items = new HashMap<>(); // this is counter for a number in item names (in items.yml) int number = 0; // check every event for (String key : ch.getConfigs().get("events").getConfig().getKeys(false)) { String instructions = ch.getString("events." + key); String[] parts = instructions.split(" "); String type = parts[0]; // if this event has items in it do the thing if (type.equals("give") || type.equals("take")) { // define all required variables String amount = ""; String conditions = ""; String material = null; int data = 0; Map<String, Integer> enchants = null; List<String> lore = null; String name = null; // for each part of the instruction string check if it // contains some data and if so pu it in variables for (String part : parts) { if (part.contains("type:")) { material = part.substring(5); } else if (part.contains("data:")) { data = Byte.valueOf(part.substring(5)); } else if (part.contains("enchants:")) { enchants = new HashMap<>(); for (String enchant : part.substring(9).split(",")) { enchants.put(enchant.split(":")[0], Integer.decode(enchant.split(":")[1])); } } else if (part.contains("lore:")) { lore = new ArrayList<>(); for (String loreLine : part.substring(5).split(";")) { lore.add(loreLine.replaceAll("_", " ")); } } else if (part.contains("name:")) { name = part.substring(5).replaceAll("_", " "); } else if (part.contains("amount:")) { amount = part; } else if (part.contains("conditions:")) { conditions = part; } } // create an item String newItemID = null; @SuppressWarnings("deprecation") QuestItem item = new QuestItem(material, data, enchants, name, lore); boolean contains = false; for (String itemKey : items.keySet()) { if (items.get(itemKey).equals(item)) { contains = true; break; } } if (!contains) { // generate new name for an item newItemID = "item" + number; number++; items.put(newItemID, item); } else { for (String itemName : items.keySet()) { if (items.get(itemName).equals(item)) { newItemID = itemName; } } } ch.getConfigs().get("events").getConfig().set(key, (type + " " + newItemID + " " + amount + " " + conditions).trim()); // replace event with updated version Debug.broadcast("Extracted " + newItemID + " from " + key + " event!"); } } // check every condition (it's almost the same code, I didn't know how // to do // it better for (String key : ch.getConfigs().get("conditions").getConfig().getKeys(false)) { String instructions = ch.getString("conditions." + key); String[] parts = instructions.split(" "); String type = parts[0]; // if this condition has items do the thing if (type.equals("hand") || type.equals("item")) { // define all variables String amount = ""; String material = null; int data = 0; Map<String, Integer> enchants = new HashMap<>(); List<String> lore = new ArrayList<>(); String name = null; String inverted = ""; // for every part check if it has some data and place it in // variables for (String part : parts) { if (part.contains("type:")) { material = part.substring(5); } else if (part.contains("data:")) { data = Byte.valueOf(part.substring(5)); } else if (part.contains("enchants:")) { for (String enchant : part.substring(9).split(",")) { enchants.put(enchant.split(":")[0], Integer.decode(enchant.split(":")[1])); } } else if (part.contains("lore:")) { for (String loreLine : part.substring(5).split(";")) { lore.add(loreLine.replaceAll("_", " ")); } } else if (part.contains("name:")) { name = part.substring(5).replaceAll("_", " "); } else if (part.contains("amount:")) { amount = part; } else if (part.equalsIgnoreCase("--inverted")) { inverted = part; } } // create an item String newItemID = null; @SuppressWarnings("deprecation") QuestItem item = new QuestItem(material, data, enchants, name, lore); boolean contains = false; for (String itemKey : items.keySet()) { if (items.get(itemKey).equals(item)) { contains = true; break; } } if (!contains) { // generate new name for an item newItemID = "item" + number; number++; items.put(newItemID, item); } else { for (String itemName : items.keySet()) { if (items.get(itemName).equals(item)) { newItemID = itemName; } } } ch.getConfigs().get("conditions").getConfig().set(key, (type + " item:" + newItemID + " " + amount + " " + inverted).trim()); Debug.broadcast("Extracted " + newItemID + " from " + key + " condition!"); } } // generated all items, now place them in items.yml for (String key : items.keySet()) { QuestItem item = items.get(key); String instruction = item.getMaterial() + " data:" + item.getData(); if (item.getName() != null) { instruction = instruction + " name:" + item.getName().replace(" ", "_"); } if (item.getLore() != null && !item.getLore().isEmpty()) { StringBuilder lore = new StringBuilder(); for (String line : item.getLore()) { lore.append(line + ";"); } instruction = instruction + " lore:" + (lore.substring(0, lore.length() - 1).replace(" ", "_")); } if (item.getEnchants() != null && !item.getEnchants().isEmpty()) { StringBuilder enchants = new StringBuilder(); for (Enchantment enchant : item.getEnchants().keySet()) { enchants.append(enchant.toString() + ":" + item.getEnchants().get(enchant) + ","); } instruction = instruction + " enchants:" + enchants.substring(0, enchants.length() - 1); } ch.getConfigs().get("items").getConfig().set(key, instruction); } ch.getConfigs().get("items").saveConfig(); ch.getConfigs().get("events").saveConfig(); ch.getConfigs().get("conditions").saveConfig(); Debug.broadcast("All extracted items has been successfully saved to items.yml!"); // end of updating to 1.4 instance.getConfig().set("version", "1.4"); Debug.broadcast("Conversion to v1.4 finished."); updateTo1_4_1(); } private void updateTo1_3() { Debug.broadcast("Started converting configuration files from unknown version to v1.3!"); // add conversion options Debug.broadcast("Using Names by for safety. If you run UUID compatible server and " + "want to use UUID, change it manually in the config file and reload the plugin."); config.set("uuid", "false"); // this will alert the plugin that the conversion should be done if UUID // is // set to true config.set("convert", "true"); // add metrics if they are not set yet if (!config.isSet("metrics")) { Debug.broadcast("Added metrics option."); config.set("metrics", "true"); } // add stop to conversation if not done already Debug.broadcast("Adding stop nodes to conversations..."); int count = 0; ConfigAccessor conversations = ch.getConfigs().get("conversations"); Set<String> convNodes = conversations.getConfig().getKeys(false); for (String convNode : convNodes) { if (!conversations.getConfig().isSet(convNode + ".stop")) { conversations.getConfig().set(convNode + ".stop", "false"); count++; } } conversations.saveConfig(); Debug.broadcast("Done, modified " + count + " conversations!"); // end of updating to 1.3 config.set("version", "1.3"); Debug.broadcast("Conversion to v1.3 finished."); updateTo1_4(); } /** * Updates language file, so it contains all required messages. */ private void updateLanguages() { // add new languages boolean isUpdated = false; ConfigAccessor messages = Config.getMessages(); // check every language if it exists for (String path : messages.getConfig().getDefaultSection().getKeys(false)) { if (messages.getConfig().isSet(path)) { // if it exists check every message if it exists for (String messageNode : messages.getConfig().getDefaults().getConfigurationSection(path) .getKeys(false)) { if (!messages.getConfig().isSet(path + "." + messageNode)) { // if message doesn't exist then add it from defaults messages.getConfig().set(path + "." + messageNode, messages.getConfig().getDefaults().get(path + "." + messageNode)); isUpdated = true; } } } else { // if language does not exist then add every message to it for (String messageNode : messages.getConfig().getDefaults().getConfigurationSection(path) .getKeys(false)) { messages.getConfig().set(path + "." + messageNode, messages.getConfig().getDefaults().get(path + "." + messageNode)); isUpdated = true; } } } // if we updated config filse then print the message if (isUpdated) { messages.saveConfig(); Debug.broadcast("Updated language files!"); } } /** * As the name says, converts all names to UUID in database */ @SuppressWarnings("deprecation") private void convertNamesToUUID() { Debug.broadcast("Converting names to UUID..."); // loop all tables HashMap<String, String> list = new HashMap<>(); String[] tables = new String[] { "OBJECTIVES", "TAGS", "POINTS", "JOURNAL", "BACKPACK" }; Connector con = new Connector(); for (String table : tables) { ResultSet res = con.querySQL(QueryType.valueOf("SELECT_PLAYERS_" + table), new String[] {}); try { while (res.next()) { // and extract from them list of player names String playerID = res.getString("playerID"); if (!list.containsKey(playerID)) { list.put(playerID, Bukkit.getOfflinePlayer(playerID).getUniqueId().toString()); } } } catch (SQLException e) { e.printStackTrace(); } } // convert all player names in all tables for (String table : tables) { for (String playerID : list.keySet()) { con.updateSQL(UpdateType.valueOf("UPDATE_PLAYERS_" + table), new String[] { list.get(playerID), playerID }); } } Debug.broadcast("Names conversion finished!"); } /** * Adds the changelog file. */ private void addChangelog() { try { File changelog = new File(BetonQuest.getInstance().getDataFolder(), "changelog.txt"); if (changelog.exists()) { changelog.delete(); } Files.copy(BetonQuest.getInstance().getResource("changelog.txt"), changelog.toPath()); Debug.broadcast("Changelog added!"); } catch (IOException e) { e.printStackTrace(); } } private String convertObjective(String obj) { StringBuilder builder = new StringBuilder(); for (String part : obj.split(" ")) { if (part.startsWith("tag:")) { builder.append("label:" + part.substring(4)); } else { builder.append(part); } builder.append(' '); } return builder.toString().trim(); } /** * Deprecated config handler, used only for configuration updating process * * @author Jakub Sapalski */ private class ConfigHandler { /** * Map containing accessors for every conversation. */ private HashMap<String, ConfigAccessor> conversationsMap = new HashMap<>(); /** * Deprecated accessor for single conversations file, used only for * updating configuration. */ private ConfigAccessor conversations; /** * Deprecated accessor for objectives file, used only for updating * configuration. */ private ConfigAccessor objectives; /** * Accessor for conditions file. */ private ConfigAccessor conditions; /** * Accessor for events file. */ private ConfigAccessor events; /** * Accessor for messages file. */ private ConfigAccessor messages; /** * Accessor for npcs file. */ private ConfigAccessor npcs; /** * Accessor for journal file. */ private ConfigAccessor journal; /** * Accessor for items file. */ private ConfigAccessor items; /** * Legacy configuration handler, only used for updating purposes. Do not * use!!! */ public ConfigHandler() { // put config accesors in fields conversations = new ConfigAccessor( new File(BetonQuest.getInstance().getDataFolder(), "conversations.yml"), "conversations.yml", AccessorType.CONVERSATION); objectives = new ConfigAccessor(new File(BetonQuest.getInstance().getDataFolder(), "objectives.yml"), "objectives.yml", AccessorType.OBJECTIVES); conditions = new ConfigAccessor(new File(BetonQuest.getInstance().getDataFolder(), "conditions.yml"), "conditions.yml", AccessorType.CONDITIONS); events = new ConfigAccessor(new File(BetonQuest.getInstance().getDataFolder(), "events.yml"), "events.yml", AccessorType.EVENTS); npcs = new ConfigAccessor(new File(BetonQuest.getInstance().getDataFolder(), "npcs.yml"), "npcs.yml", AccessorType.MAIN); journal = new ConfigAccessor(new File(BetonQuest.getInstance().getDataFolder(), "journal.yml"), "journal.yml", AccessorType.JOURNAL); items = new ConfigAccessor(new File(BetonQuest.getInstance().getDataFolder(), "items.yml"), "items.yml", AccessorType.ITEMS); messages = new ConfigAccessor(new File(BetonQuest.getInstance().getDataFolder(), "messages.yml"), "messages.yml", AccessorType.OTHER); if (new File(BetonQuest.getInstance().getDataFolder(), "conversations").exists()) { // put conversations accessors in the hashmap for (File file : new File(BetonQuest.getInstance().getDataFolder(), "conversations").listFiles()) { conversationsMap.put(file.getName().substring(0, file.getName().indexOf(".")), new ConfigAccessor(file, file.getName(), AccessorType.CONVERSATION)); } } } /** * Retireves from configuration the string at supplied path. The path * should follow this syntax: * "filename.branch.(moreBranches).branch.variable". For example getting * color for day in journal date would be * "config.journal_colors.date.day". Everything should be handled as a * string for simplicity's sake. * * @param rawPath * path for the variable * @return the String object representing requested variable */ public String getString(String rawPath) { // get parts of path String[] parts = rawPath.split("\\."); String first = parts[0]; String path = rawPath.substring(first.length() + 1); String object; // for every possible file try to access the path and return String // object switch (first) { case "config": object = BetonQuest.getInstance().getConfig().getString(path); if (object == null) { // if object is null then there is no such variable at // specified path Debug.info("Error while accessing path: " + rawPath); } return object; case "conversations": object = null; // conversations should be handled with one more level, as they // are in // multiple files String conversationID = path.split("\\.")[0]; String rest = path.substring(path.indexOf(".") + 1); if (conversationsMap.get(conversationID) != null) { object = conversationsMap.get(conversationID).getConfig().getString(rest); } if (object == null) { Debug.info("Error while accessing path: " + rawPath); } return object; case "objectives": object = objectives.getConfig().getString(path); if (object == null) { Debug.info("Error while accessing path: " + rawPath); } return object; case "conditions": object = conditions.getConfig().getString(path); if (object == null) { Debug.info("Error while accessing path: " + rawPath); } return object; case "events": object = events.getConfig().getString(path); if (object == null) { Debug.info("Error while accessing path: " + rawPath); } return object; case "messages": object = messages.getConfig().getString(path); if (object == null) { Debug.info("Error while accessing path: " + rawPath); } return object; case "npcs": object = npcs.getConfig().getString(path); return object; case "journal": object = journal.getConfig().getString(path); if (object == null) { Debug.info("Error while accessing path: " + rawPath); } return object; case "items": object = items.getConfig().getString(path); if (object == null) { Debug.info("Error while accessing path: " + rawPath); } return object; default: Debug.info("Fatal error while accessing path: " + rawPath + " (there is no such file)"); return null; } } /** * Retrieves a map containing all config accessors. Should be used for * more advanced tasks than simply getting a String. Note that * conversations are not included in this map. See * {@link #getConversations() getConversations} method for that. * Conversations accessor included in this map is just a deprecated old * conversations file. The same situation is with unused objectives * accessor. * * @return HashMap containing all config accessors */ public HashMap<String, ConfigAccessor> getConfigs() { HashMap<String, ConfigAccessor> map = new HashMap<>(); map.put("conversations", conversations); map.put("conditions", conditions); map.put("events", events); map.put("objectives", objectives); map.put("journal", journal); map.put("messages", messages); map.put("npcs", npcs); map.put("items", items); return map; } /** * Retrieves map containing all conversation accessors. * * @return HashMap containing conversation accessors */ public HashMap<String, ConfigAccessor> getConversations() { return conversationsMap; } } }