diff --git a/src/main/java/net/knarcraft/stargate/Stargate.java b/src/main/java/net/knarcraft/stargate/Stargate.java index 3fea320..d1d44f5 100644 --- a/src/main/java/net/knarcraft/stargate/Stargate.java +++ b/src/main/java/net/knarcraft/stargate/Stargate.java @@ -7,6 +7,7 @@ import net.knarcraft.stargate.config.EconomyConfig; import net.knarcraft.stargate.config.MessageSender; import net.knarcraft.stargate.config.StargateConfig; import net.knarcraft.stargate.config.StargateGateConfig; +import net.knarcraft.stargate.config.StargateYamlConfiguration; import net.knarcraft.stargate.container.BlockChangeRequest; import net.knarcraft.stargate.container.ChunkUnloadRequest; import net.knarcraft.stargate.listener.BlockEventListener; @@ -26,6 +27,7 @@ import net.knarcraft.stargate.thread.StarGateThread; import net.knarcraft.stargate.utility.BStatsHelper; import org.bukkit.Server; import org.bukkit.command.PluginCommand; +import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginManager; @@ -34,6 +36,7 @@ import org.bukkit.plugin.java.JavaPluginLoader; import org.bukkit.scheduler.BukkitScheduler; import java.io.File; +import java.io.IOException; import java.util.LinkedList; import java.util.PriorityQueue; import java.util.Queue; @@ -70,6 +73,7 @@ along with this program. If not, see . @SuppressWarnings("unused") public class Stargate extends JavaPlugin { + private static File configFile; private static final Queue blockChangeRequestQueue = new LinkedList<>(); private static final Queue chunkUnloadQueue = new PriorityQueue<>(); @@ -79,6 +83,7 @@ public class Stargate extends JavaPlugin { private static PluginManager pluginManager; private static StargateConfig stargateConfig; private static String updateAvailable = null; + private FileConfiguration configuration; /** * Empty constructor necessary for Spigot @@ -330,6 +335,26 @@ public class Stargate extends JavaPlugin { return stargateConfig.getEconomyConfig(); } + /** + * Gets the raw configuration + * + * @return

The raw configuration

+ */ + public FileConfiguration getConfiguration() { + return this.configuration; + } + + @Override + public void reloadConfig() { + super.reloadConfig(); + this.configuration = new StargateYamlConfiguration(); + try { + configuration.load(configFile); + } catch (IOException | InvalidConfigurationException e) { + Stargate.logSevere(e.getMessage()); + } + } + @Override public void onDisable() { PortalHandler.closeAllPortals(); @@ -340,11 +365,17 @@ public class Stargate extends JavaPlugin { @Override public void onEnable() { + configFile = new File(this.getDataFolder(), "config.yml"); PluginDescriptionFile pluginDescriptionFile = this.getDescription(); pluginManager = getServer().getPluginManager(); - FileConfiguration newConfig = this.getConfig(); + configuration = new StargateYamlConfiguration(); + try { + configuration.load(configFile); + } catch (IOException | InvalidConfigurationException e) { + Stargate.logSevere(e.getMessage()); + } this.saveDefaultConfig(); - newConfig.options().copyDefaults(true); + configuration.options().copyDefaults(true); logger = Logger.getLogger("Minecraft"); Server server = getServer(); diff --git a/src/main/java/net/knarcraft/stargate/command/CommandConfig.java b/src/main/java/net/knarcraft/stargate/command/CommandConfig.java index 6374518..2ae0691 100644 --- a/src/main/java/net/knarcraft/stargate/command/CommandConfig.java +++ b/src/main/java/net/knarcraft/stargate/command/CommandConfig.java @@ -66,7 +66,7 @@ public class CommandConfig implements CommandExecutor { * @param value

The new value of the config option

*/ private void updateConfigValue(ConfigOption selectedOption, CommandSender commandSender, String value) { - FileConfiguration configuration = Stargate.getInstance().getConfig(); + FileConfiguration configuration = Stargate.getInstance().getConfiguration(); //Validate any sign colors if (ConfigTag.COLOR.isTagged(selectedOption)) { @@ -162,7 +162,7 @@ public class CommandConfig implements CommandExecutor { * @param arguments

The arguments for the new config option

*/ private void updateListConfigValue(ConfigOption selectedOption, CommandSender commandSender, String[] arguments) { - FileConfiguration configuration = Stargate.getInstance().getConfig(); + FileConfiguration configuration = Stargate.getInstance().getConfiguration(); if (selectedOption == ConfigOption.PER_SIGN_COLORS) { if (arguments.length < 4) { diff --git a/src/main/java/net/knarcraft/stargate/config/StargateConfig.java b/src/main/java/net/knarcraft/stargate/config/StargateConfig.java index 99b0096..ea8598d 100644 --- a/src/main/java/net/knarcraft/stargate/config/StargateConfig.java +++ b/src/main/java/net/knarcraft/stargate/config/StargateConfig.java @@ -361,7 +361,7 @@ public final class StargateConfig { */ public void loadConfig() { Stargate.getInstance().reloadConfig(); - FileConfiguration newConfig = Stargate.getInstance().getConfig(); + FileConfiguration newConfig = Stargate.getInstance().getConfiguration(); boolean isMigrating = false; if (newConfig.getString("lang") != null || newConfig.getString("economy.freeGatesGreen") != null || diff --git a/src/main/java/net/knarcraft/stargate/config/StargateYamlConfiguration.java b/src/main/java/net/knarcraft/stargate/config/StargateYamlConfiguration.java new file mode 100644 index 0000000..2b5f751 --- /dev/null +++ b/src/main/java/net/knarcraft/stargate/config/StargateYamlConfiguration.java @@ -0,0 +1,155 @@ +package net.knarcraft.stargate.config; + +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * A YAML configuration which keeps all comments + * + * @author Thorin + */ +public class StargateYamlConfiguration extends YamlConfiguration { + + static public final String END_OF_COMMENT = "_endOfComment_"; + static public final String START_OF_COMMENT = "comment_"; + + @Override + public @NotNull String saveToString() { + return this.convertYAMLMappingsToComments(super.saveToString()); + } + + @Override + public void loadFromString(@NotNull String contents) throws InvalidConfigurationException { + super.loadFromString(this.convertCommentsToYAMLMappings(contents)); + } + + /** + * Reads a file with comments, and recreates them into yaml mappings + * + *

A mapping follows this format: comment_{CommentNumber}: "The comment" + * This needs to be done as comments otherwise get removed using + * the {@link FileConfiguration#save(File)} method. The config + * needs to be saved if a config value has changed.

+ */ + private String convertCommentsToYAMLMappings(String configString) { + StringBuilder yamlBuilder = new StringBuilder(); + List currentComment = new ArrayList<>(); + int commentId = 0; + + for (String line : configString.split("\n")) { + if (line.trim().startsWith("#")) { + //Temporarily store the comment line + currentComment.add(line.trim().replaceFirst("#", "")); + } else { + //Write the full formatted comment to the StringBuilder + if (!currentComment.isEmpty()) { + int indentation = getIndentation(line); + generateCommentYAML(yamlBuilder, currentComment, commentId++, indentation); + currentComment = new ArrayList<>(); + } + //Add the non-comment line assuming it isn't empty + if (!line.trim().isEmpty()) { + yamlBuilder.append(line).append("\n"); + } + } + } + return yamlBuilder.toString(); + } + + /** + * Generates a YAML-compatible string for one comment block + * + * @param yamlBuilder

The string builder to add the generated YAML to

+ * @param commentLines

The lines of the comment to convert into YAML

+ * @param commentId

The unique id of the comment

+ * @param indentation

The indentation to add to every line

+ */ + private void generateCommentYAML(StringBuilder yamlBuilder, List commentLines, int commentId, int indentation) { + String subIndentation = this.addIndentation(indentation + 2); + //Add the comment start marker + yamlBuilder.append(this.addIndentation(indentation)).append(START_OF_COMMENT).append(commentId).append(": |\n"); + for (String commentLine : commentLines) { + //Add each comment line with the proper indentation + yamlBuilder.append(subIndentation).append(commentLine).append("\n"); + } + //Add the comment end marker + yamlBuilder.append(subIndentation).append(subIndentation).append(END_OF_COMMENT).append("\n"); + } + + /** + * Converts the internal YAML mapping format to a readable config file + * + *

The internal YAML structure is converted to a string with the same format as a standard configuration file. + * The internal structure has comments in the format: START_OF_COMMENT + id + multi-line YAML string + + * END_OF_COMMENT.

+ * + * @param yamlString

A string using the YAML format

+ * @return

The corresponding comment string

+ */ + private String convertYAMLMappingsToComments(String yamlString) { + StringBuilder finalText = new StringBuilder(); + boolean isReadingCommentBlock = false; + int commentIndentation = 0; + for (String line : yamlString.split("\n")) { + String possibleComment = line.trim(); + + if (isReadingCommentBlock && line.contains(END_OF_COMMENT)) { + //Skip the line signifying the end of a comment + isReadingCommentBlock = false; + } else if (possibleComment.startsWith(START_OF_COMMENT)) { + //Skip the comment start line, and start comment parsing + isReadingCommentBlock = true; + //Get the indentation to use for the comment block + commentIndentation = getIndentation(line); + //Add an empty line before every comment block + finalText.append("\n"); + } else if (line.isEmpty() && !isReadingCommentBlock) { + //Output the empty line as-is, as it's not part of a comment + finalText.append("\n"); + } else if (isReadingCommentBlock) { + //Output the comment with correct indentation + finalText.append(addIndentation(commentIndentation)).append("# ").append(possibleComment).append("\n"); + } else { + //Output the configuration key + finalText.append(line).append("\n"); + } + } + return finalText.toString().trim(); + } + + /** + * Gets a string containing the given indentation + * + * @param indentationSpaces

The number spaces to use for indentation

+ * @return

A string containing the number of spaces specified

+ */ + private String addIndentation(int indentationSpaces) { + return " ".repeat(Math.max(0, indentationSpaces)); + } + + + /** + * Gets the indentation (number of spaces) of the given line + * + * @param line

The line to get indentation of

+ * @return

The number of spaces in the line's indentation

+ */ + private int getIndentation(String line) { + int spacesFound = 0; + for (char aCharacter : line.toCharArray()) { + if (aCharacter == ' ') { + spacesFound++; + } else { + break; + } + } + return spacesFound; + } + +} diff --git a/src/main/resources/config-migrations.txt b/src/main/resources/config-migrations.txt index b898212..43893cd 100644 --- a/src/main/resources/config-migrations.txt +++ b/src/main/resources/config-migrations.txt @@ -1,5 +1,5 @@ lang=language -defaultNetwork=defaultGateNetwork +defaultNetwork=gates.defaultGateNetwork use-mysql= ignoreEntrance= enableEconomy=economy.useEconomy @@ -17,7 +17,7 @@ sortLists=gates.cosmetic.sortNetworkDestinations protectEntrance=gates.integrity.protectEntrance enableBungee=gates.functionality.enableBungee verifyPortals=gates.integrity.verifyPortals -signColor=gates.cosmetic.signColor +signColor=gates.cosmetic.mainSignColor gates.cosmetic.signColor=gates.cosmetic.mainSignColor debug=debugging.debug permdebug=debugging.permissionDebug