From 6d95af39634ee499fd25b88b9ab214136bea1a1e Mon Sep 17 00:00:00 2001 From: Lennart ten Wolde <0p1q9o2w@hotmail.nl> Date: Thu, 10 Mar 2016 17:40:40 +0100 Subject: [PATCH] Add config wrapper to update config Configuration reads old config and moves it's values over to a new config copied from the resources directory. normally, when you save it would delete the comments, but they are written back in to the new config by this wrapper now people their config files will actually update --- .../us/myles/ViaVersion/ViaVersionPlugin.java | 28 +- .../myles/ViaVersion/util/Configuration.java | 246 ++++++++++++++++++ 2 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 src/main/java/us/myles/ViaVersion/util/Configuration.java diff --git a/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java b/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java index 7b190cc48..df873c329 100644 --- a/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java +++ b/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java @@ -24,9 +24,12 @@ import us.myles.ViaVersion.handlers.ViaVersionInitializer; import us.myles.ViaVersion.listeners.CommandBlockListener; import us.myles.ViaVersion.update.UpdateListener; import us.myles.ViaVersion.update.UpdateUtil; +import us.myles.ViaVersion.util.Configuration; import us.myles.ViaVersion.util.ListWrapper; import us.myles.ViaVersion.util.ReflectionUtil; +import java.io.File; +import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.List; @@ -36,6 +39,7 @@ import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.logging.Level; public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI { @@ -64,7 +68,7 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI { @Override public void onEnable() { ViaVersion.setInstance(this); - saveDefaultConfig(); + generateConfig(); if (System.getProperty("ViaVersion") != null) { getLogger().severe("ViaVersion is already loaded, we don't support reloads. Please reboot if you wish to update."); getLogger().severe("Some features may not work."); @@ -93,6 +97,28 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI { getCommand("viaversion").setExecutor(new ViaVersionCommand(this)); } + public void generateConfig() { + File file = new File(getDataFolder(), "config.yml"); + if(file.exists()) { + // Update config options + Configuration oldConfig = new Configuration(file); + oldConfig.reload(false); // Load current options from config + file.delete(); // Delete old config + saveDefaultConfig(); // Generate new config + Configuration newConfig = new Configuration(file); + newConfig.reload(true); // Load default options + for(String key : oldConfig.getKeys(false)) { + // Set option in new config if exists + if(newConfig.contains(key)) { + newConfig.set(key, oldConfig.get(key)); + } + } + newConfig.save(); + } else { + saveDefaultConfig(); + } + } + public void injectPacketHandler() { try { Class serverClazz = ReflectionUtil.nms("MinecraftServer"); diff --git a/src/main/java/us/myles/ViaVersion/util/Configuration.java b/src/main/java/us/myles/ViaVersion/util/Configuration.java new file mode 100644 index 000000000..ee1c7c808 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/util/Configuration.java @@ -0,0 +1,246 @@ +package us.myles.ViaVersion.util; + +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.bukkit.Bukkit; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.regex.Pattern; + +public class Configuration extends YamlConfiguration { + private List mainHeader = Lists.newArrayList(); + private final Map> headers = Maps.newConcurrentMap(); + private final File file; + private boolean loadHeaders; + + public Configuration(File file) { + this.file = file; + } + + /** + * Set the main header displayed at top of config. + * + * @param header header + */ + public void mainHeader(String... header) { + mainHeader = Arrays.asList(header); + } + + /** + * Get main header displayed at top of config. + * + * @return header + */ + public List mainHeader() { + return mainHeader; + } + + /** + * Set option header. + * + * @param key of option (or section) + * @param header of option (or section) + */ + public void header(String key, String... header) { +// String value = Joiner.on('\n').join(header); + headers.put(key, Arrays.asList(header)); + } + + /** + * Get header of option + * + * @param key of option (or section) + * @return Header + */ + public List header(String key) { + return headers.get(key); + } + + public T get(String key, Class type) { + return type.cast(get(key)); + } + + /** + * Reload config from file. + */ + public void reload() { + reload(headers.isEmpty()); + } + + /** + * Reload config from file. + * + * @param loadHeaders Whether or not to load headers. + */ + public void reload(boolean loadHeaders) { + this.loadHeaders = loadHeaders; + try { + load(file); + } catch(Exception e) { + Bukkit.getLogger().log(Level.WARNING, "failed to reload file", e); + } + } + + @Override + public void loadFromString(String contents) throws InvalidConfigurationException { + if(!loadHeaders) { + super.loadFromString(contents); + return; + } + + StringBuilder memoryData = new StringBuilder(); + + // Parse headers + final int indentLength = options().indent(); + final String pathSeparator = Character.toString(options().pathSeparator()); + int currentIndents = 0; + String key = ""; + List headers = Lists.newArrayList(); + for(String line : contents.split("\n")) { + if(line.isEmpty()) continue; // Skip empty lines + int indent = getSuccessiveCharCount(line, ' '); + String subline = indent > 0 ? line.substring(indent) : line; + if(subline.startsWith("#")) { + if(subline.startsWith("#>")) { + String txt = subline.startsWith("#> ") ? subline.substring(3) : subline.substring(2); + mainHeader.add(txt); + continue; // Main header, handled by bukkit + } + + // Add header to list + String txt = subline.startsWith("# ") ? subline.substring(2) : subline.substring(1); + headers.add(txt); + continue; + } + + int indents = indent / indentLength; + if(indents <= currentIndents) { + // Remove last section of key + String[] array = key.split(Pattern.quote(pathSeparator)); + int backspace = currentIndents - indents + 1; + key = join(array, options().pathSeparator(), 0, array.length - backspace); + } + + // Add new section to key + String separator = key.length() > 0 ? pathSeparator : ""; + String lineKey = line.contains(":") ? line.split(Pattern.quote(":"))[0] : line; + key += separator + lineKey.substring(indent); + + currentIndents = indents; + + memoryData.append(line).append('\n'); + if(!headers.isEmpty()) { + this.headers.put(key, headers); + headers = Lists.newArrayList(); + } + } + + // Parse remaining text + super.loadFromString(memoryData.toString()); + + // Clear bukkit header + options().header(null); + } + + /** + * Save config to file + */ + public void save() { + if(headers.isEmpty() && mainHeader.isEmpty()) { + try { + super.save(file); + } catch(IOException e) { + Bukkit.getLogger().log(Level.WARNING, "Failed to save file", e); + } + return; + } + + // Custom save + final int indentLength = options().indent(); + final String pathSeparator = Character.toString(options().pathSeparator()); + String content = saveToString(); + StringBuilder fileData = new StringBuilder(buildHeader()); + int currentIndents = 0; + String key = ""; + for(String h : mainHeader) { + // Append main header to top of file + fileData.append("#> ").append(h).append('\n'); + } + + for(String line : content.split("\n")) { + if(line.isEmpty()) continue; // Skip empty lines + int indent = getSuccessiveCharCount(line, ' '); + int indents = indent / indentLength; + String indentText = indent > 0 ? line.substring(0, indent) : ""; + if(indents <= currentIndents) { + // Remove last section of key + String[] array = key.split(Pattern.quote(pathSeparator)); + int backspace = currentIndents - indents + 1; + key = join(array, options().pathSeparator(), 0, array.length - backspace); + } + + // Add new section to key + String separator = key.length() > 0 ? pathSeparator : ""; + String lineKey = line.contains(":") ? line.split(Pattern.quote(":"))[0] : line; + key += separator + lineKey.substring(indent); + + currentIndents = indents; + + List header = headers.get(key); + String headerText = header != null ? addHeaderTags(header, indentText) : ""; + fileData.append(headerText).append(line).append('\n'); + } + + // Write data to file + FileWriter writer = null; + try { + writer = new FileWriter(file); + writer.write(fileData.toString()); + writer.flush(); + } catch(IOException e) { + Bukkit.getLogger().log(Level.WARNING, "Failed to save file", e); + } finally { + if(writer != null) { + try { + writer.close(); + } catch(IOException e) { + } + } + } + } + + private String addHeaderTags(List header, String indent) { + StringBuilder builder = new StringBuilder(); + for(String line : header) { + builder.append(indent).append("# ").append(line).append('\n'); + } + return builder.toString(); + } + + private String join(String[] array, char joinChar, int start, int length) { + String[] copy = new String[length - start]; + System.arraycopy(array, start, copy, 0, length - start); + return Joiner.on(joinChar).join(copy); + } + + private int getSuccessiveCharCount(String text, char key) { + int count = 0; + for(int i = 0; i < text.length(); i++) { + if(text.charAt(i) == key) { + count += 1; + } else { + break; + } + } + return count; + } +}